@openclaw/codex 2026.7.1-beta.2 → 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-B7hqD3Xj.js → app-server-policy-BUk0GLMy.js} +5 -5
- package/dist/{attempt-notifications-tqyQ_Tdn.js → attempt-notifications-BGsEIIDI.js} +5 -40
- package/dist/{command-handlers-C9K-KRU-.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-CpurU-G9.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 +1117 -19
- package/dist/{media-understanding-provider-BOeoBhEw.js → media-understanding-provider-DgqRvU2_.js} +13 -8
- package/dist/media-understanding-provider.js +1 -1
- package/dist/{models-DpzY1xpp.js → models-DDmO1zwd.js} +2 -2
- package/dist/notification-correlation-Bo7KB3ks.js +35 -0
- package/dist/{plugin-app-cache-key-BrZUG9jj.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-CJiWigAJ.js → protocol-validators-dZQ-UTOa.js} +116 -59
- package/dist/{provider-capabilities-wqedK49Z.js → provider-capabilities-gTCwjfmh.js} +2916 -114
- package/dist/{provider-nmtMDcGH.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-DikbXia_.js → run-attempt-8SPLiDlT.js} +907 -3002
- 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-Ca9wqmd0.js → side-question-DkjXvYv7.js} +132 -140
- package/dist/{thread-lifecycle-D6R40plk.js → thread-lifecycle-BskXnNP-.js} +609 -444
- package/dist/{web-search-provider.runtime-B6eZEFmd.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-DfFu9JH_.js +0 -15
- package/dist/compact-CILptrPS.js +0 -278
- package/dist/conversation-binding-DOJtUxOy.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-CpurU-G9.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;
|
|
@@ -3079,2574 +3305,106 @@ function createCodexAttemptTurnWatchController(params) {
|
|
|
3079
3305
|
});
|
|
3080
3306
|
params.onAbort("turn_completion_idle_timeout");
|
|
3081
3307
|
}
|
|
3082
|
-
function fireTerminalIdleTimeout() {
|
|
3083
|
-
if (params.isCompleted() || params.isTerminalTurnNotificationQueued() || params.signal.aborted || !terminalIdleWatchArmed || params.getActiveAppServerTurnRequests() > 0) return;
|
|
3084
|
-
const idleMs = Math.max(0, Date.now() - completionLastActivityAt);
|
|
3085
|
-
if (idleMs < turnTerminalIdleTimeoutMs) {
|
|
3086
|
-
scheduleTerminalIdleWatch();
|
|
3087
|
-
return;
|
|
3088
|
-
}
|
|
3089
|
-
const timeout = {
|
|
3090
|
-
kind: "terminal",
|
|
3091
|
-
idleMs,
|
|
3092
|
-
timeoutMs: turnTerminalIdleTimeoutMs,
|
|
3093
|
-
lastActivityReason: completionLastActivityReason,
|
|
3094
|
-
details: completionLastActivityDetails
|
|
3095
|
-
};
|
|
3096
|
-
params.onTimeout(timeout);
|
|
3097
|
-
params.onMarkTimedOut();
|
|
3098
|
-
params.onRecordEvent("turn.terminal_idle_timeout", {
|
|
3099
|
-
threadId: params.threadId,
|
|
3100
|
-
turnId: params.getTurnId(),
|
|
3101
|
-
idleMs,
|
|
3102
|
-
timeoutMs: timeout.timeoutMs,
|
|
3103
|
-
lastActivityReason: timeout.lastActivityReason,
|
|
3104
|
-
...timeout.details
|
|
3105
|
-
});
|
|
3106
|
-
embeddedAgentLog.warn("codex app-server turn idle timed out waiting for terminal event", {
|
|
3107
|
-
threadId: params.threadId,
|
|
3108
|
-
turnId: params.getTurnId(),
|
|
3109
|
-
idleMs,
|
|
3110
|
-
timeoutMs: timeout.timeoutMs,
|
|
3111
|
-
lastActivityReason: timeout.lastActivityReason,
|
|
3112
|
-
...timeout.details
|
|
3113
|
-
});
|
|
3114
|
-
params.onAbort("turn_terminal_idle_timeout");
|
|
3115
|
-
}
|
|
3116
|
-
return {
|
|
3117
|
-
isCompletionIdleWatchArmed: () => completionIdleWatchArmed,
|
|
3118
|
-
isCompletionIdleWatchPinnedByTerminalError: () => completionIdleWatchPinnedByTerminalError,
|
|
3119
|
-
isAssistantCompletionIdleWatchArmed: () => assistantCompletionIdleWatchArmed,
|
|
3120
|
-
armAttemptIdleWatch: () => {
|
|
3121
|
-
attemptIdleWatchArmed = true;
|
|
3122
|
-
scheduleAttemptIdleWatch();
|
|
3123
|
-
},
|
|
3124
|
-
armTerminalIdleWatch: () => {
|
|
3125
|
-
terminalIdleWatchArmed = true;
|
|
3126
|
-
scheduleTerminalIdleWatch();
|
|
3127
|
-
},
|
|
3128
|
-
armCompletionIdleWatch: (options) => {
|
|
3129
|
-
completionIdleWatchArmed = true;
|
|
3130
|
-
completionIdleWatchPinnedByTerminalError = options?.pinnedByTerminalError === true;
|
|
3131
|
-
completionIdleTimeoutOverrideMs = options?.timeoutMs !== void 0 ? resolveWatchTimeoutMs(options.timeoutMs) : void 0;
|
|
3132
|
-
scheduleCompletionIdleWatch();
|
|
3133
|
-
},
|
|
3134
|
-
disarmCompletionIdleWatch: () => {
|
|
3135
|
-
completionIdleWatchArmed = false;
|
|
3136
|
-
completionIdleWatchPinnedByTerminalError = false;
|
|
3137
|
-
completionIdleTimeoutOverrideMs = void 0;
|
|
3138
|
-
clearCompletionIdleTimer();
|
|
3139
|
-
},
|
|
3140
|
-
armAssistantCompletionIdleWatch: (details) => {
|
|
3141
|
-
assistantCompletionIdleWatchArmed = true;
|
|
3142
|
-
assistantCompletionLastActivityAt = Date.now();
|
|
3143
|
-
assistantCompletionLastActivityDetails = details;
|
|
3144
|
-
scheduleAssistantCompletionIdleWatch();
|
|
3145
|
-
},
|
|
3146
|
-
disarmAssistantCompletionIdleWatch: () => {
|
|
3147
|
-
assistantCompletionIdleWatchArmed = false;
|
|
3148
|
-
assistantCompletionLastActivityDetails = void 0;
|
|
3149
|
-
clearAssistantCompletionIdleTimer();
|
|
3150
|
-
},
|
|
3151
|
-
touchActivity: (reason, options) => {
|
|
3152
|
-
completionLastActivityAt = Date.now();
|
|
3153
|
-
completionLastActivityReason = reason;
|
|
3154
|
-
completionLastActivityDetails = options?.details;
|
|
3155
|
-
completionIdleTimeoutOverrideMs = void 0;
|
|
3156
|
-
if (options?.attemptProgress) recordAttemptProgress(reason, options);
|
|
3157
|
-
params.onProgressDiagnostic(reason);
|
|
3158
|
-
if (options?.arm) {
|
|
3159
|
-
completionIdleWatchArmed = true;
|
|
3160
|
-
completionIdleWatchPinnedByTerminalError = false;
|
|
3161
|
-
}
|
|
3162
|
-
scheduleProgressWatches();
|
|
3163
|
-
},
|
|
3164
|
-
noteNotificationReceived: (method, options) => {
|
|
3165
|
-
completionLastActivityAt = Date.now();
|
|
3166
|
-
completionLastActivityReason = `notification:${method}`;
|
|
3167
|
-
if (options?.details !== void 0) completionLastActivityDetails = options.details;
|
|
3168
|
-
if (options?.attemptProgress) recordAttemptProgress(completionLastActivityReason, options);
|
|
3169
|
-
},
|
|
3170
|
-
extendAttemptIdleWatch: (timeoutMs) => {
|
|
3171
|
-
attemptIdleTimeoutOverrideMs = resolveWatchTimeoutMs(timeoutMs);
|
|
3172
|
-
scheduleAttemptIdleWatch();
|
|
3173
|
-
},
|
|
3174
|
-
scheduleProgressWatches,
|
|
3175
|
-
clearCompletionIdleTimer,
|
|
3176
|
-
clearAssistantCompletionIdleTimer,
|
|
3177
|
-
clearTerminalIdleTimer,
|
|
3178
|
-
clearAttemptIdleTimer,
|
|
3179
|
-
clearAllTimers
|
|
3180
|
-
};
|
|
3181
|
-
}
|
|
3182
|
-
/** Hard cap for per-call Codex dynamic tool timeout overrides. */
|
|
3183
|
-
const CODEX_DYNAMIC_TOOL_MAX_TIMEOUT_MS = 6e5;
|
|
3184
|
-
const CODEX_DYNAMIC_IMAGE_GENERATION_TOOL_TIMEOUT_MS = 12e4;
|
|
3185
|
-
/** Timeout for message-delivery dynamic tool calls. */
|
|
3186
|
-
const CODEX_DYNAMIC_MESSAGE_TOOL_TIMEOUT_MS = 12e4;
|
|
3187
|
-
const LOG_FIELD_MAX_LENGTH = 160;
|
|
3188
|
-
function normalizeLogField(value) {
|
|
3189
|
-
if (typeof value !== "string") return;
|
|
3190
|
-
const normalized = value.replaceAll(String.fromCharCode(27), " ").replaceAll("\r", " ").replaceAll("\n", " ").replaceAll(" ", " ").trim();
|
|
3191
|
-
if (!normalized) return;
|
|
3192
|
-
return normalized.length > LOG_FIELD_MAX_LENGTH ? `${normalized.slice(0, LOG_FIELD_MAX_LENGTH - 3)}...` : normalized;
|
|
3193
|
-
}
|
|
3194
|
-
function readNumericTimeoutMs(value) {
|
|
3195
|
-
if (typeof value === "number" && Number.isFinite(value)) return Math.max(0, Math.floor(value));
|
|
3196
|
-
if (typeof value === "string") {
|
|
3197
|
-
const parsed = parseStrictNonNegativeInteger(value);
|
|
3198
|
-
if (parsed !== void 0) return Math.max(0, Math.floor(parsed));
|
|
3199
|
-
}
|
|
3200
|
-
}
|
|
3201
|
-
function formatDynamicToolTimeoutDetails(params) {
|
|
3202
|
-
const tool = normalizeLogField(params.call.tool) ?? "unknown";
|
|
3203
|
-
const baseMeta = {
|
|
3204
|
-
tool: params.call.tool,
|
|
3205
|
-
toolCallId: params.call.callId,
|
|
3206
|
-
threadId: params.call.threadId,
|
|
3207
|
-
turnId: params.call.turnId,
|
|
3208
|
-
timeoutMs: params.timeoutMs,
|
|
3209
|
-
timeoutKind: "codex_dynamic_tool_rpc"
|
|
3210
|
-
};
|
|
3211
|
-
if (tool !== "process" || !isJsonObject(params.call.arguments)) return {
|
|
3212
|
-
responseMessage: `OpenClaw dynamic tool call timed out after ${params.timeoutMs}ms while running tool ${tool}.`,
|
|
3213
|
-
consoleMessage: `codex dynamic tool timeout: tool=${tool} toolTimeoutMs=${params.timeoutMs}; per-tool-call watchdog, not session idle`,
|
|
3214
|
-
meta: baseMeta
|
|
3215
|
-
};
|
|
3216
|
-
const action = normalizeLogField(params.call.arguments.action);
|
|
3217
|
-
const sessionId = normalizeLogField(params.call.arguments.sessionId);
|
|
3218
|
-
const requestedTimeoutMs = readNumericTimeoutMs(params.call.arguments.timeout);
|
|
3219
|
-
const actionPart = action ? ` action=${action}` : "";
|
|
3220
|
-
const sessionPart = sessionId ? ` sessionId=${sessionId}` : "";
|
|
3221
|
-
const requestedPart = requestedTimeoutMs === void 0 ? "" : ` requestedWaitMs=${requestedTimeoutMs}`;
|
|
3222
|
-
const retryHint = action === "poll" ? "; repeated lines usually mean process-poll retry churn, not model progress" : "";
|
|
3223
|
-
const responseTarget = action || sessionId ? ` while waiting for process${actionPart}${sessionPart}` : " while waiting for the process tool";
|
|
3224
|
-
return {
|
|
3225
|
-
responseMessage: `OpenClaw dynamic tool call timed out after ${params.timeoutMs}ms${responseTarget}. This is a tool RPC timeout, not a session idle timeout.`,
|
|
3226
|
-
consoleMessage: `codex process tool timeout:${actionPart}${sessionPart} toolTimeoutMs=${params.timeoutMs}${requestedPart}; per-tool-call watchdog, not session idle${retryHint}`,
|
|
3227
|
-
meta: {
|
|
3228
|
-
...baseMeta,
|
|
3229
|
-
processAction: action,
|
|
3230
|
-
processSessionId: sessionId,
|
|
3231
|
-
processRequestedTimeoutMs: requestedTimeoutMs
|
|
3232
|
-
}
|
|
3233
|
-
};
|
|
3234
|
-
}
|
|
3235
|
-
/**
|
|
3236
|
-
* Runs a dynamic tool call with run-abort and per-call timeout handling,
|
|
3237
|
-
* returning a Codex protocol response instead of throwing.
|
|
3238
|
-
*/
|
|
3239
|
-
async function handleDynamicToolCallWithTimeout(params) {
|
|
3240
|
-
let didNotifyAgentToolResult = false;
|
|
3241
|
-
const notifyAgentToolResult = (event) => {
|
|
3242
|
-
if (didNotifyAgentToolResult) return;
|
|
3243
|
-
didNotifyAgentToolResult = true;
|
|
3244
|
-
try {
|
|
3245
|
-
params.onAgentToolResult?.(event);
|
|
3246
|
-
} catch (error) {
|
|
3247
|
-
embeddedAgentLog.warn(`onAgentToolResult handler failed: tool=${params.call.tool} error=${String(error)}`);
|
|
3248
|
-
}
|
|
3249
|
-
};
|
|
3250
|
-
const notifyFailedToolResult = (message) => {
|
|
3251
|
-
notifyAgentToolResult({
|
|
3252
|
-
toolName: params.call.tool,
|
|
3253
|
-
result: {
|
|
3254
|
-
content: [{
|
|
3255
|
-
type: "text",
|
|
3256
|
-
text: message
|
|
3257
|
-
}],
|
|
3258
|
-
details: {
|
|
3259
|
-
status: "failed",
|
|
3260
|
-
error: message
|
|
3261
|
-
}
|
|
3262
|
-
},
|
|
3263
|
-
isError: true
|
|
3264
|
-
});
|
|
3265
|
-
};
|
|
3266
|
-
if (params.signal.aborted) {
|
|
3267
|
-
const message = "OpenClaw dynamic tool call aborted before execution.";
|
|
3268
|
-
params.onFallbackSelected?.();
|
|
3269
|
-
notifyFailedToolResult(message);
|
|
3270
|
-
return failedDynamicToolResponse(message);
|
|
3271
|
-
}
|
|
3272
|
-
const controller = new AbortController();
|
|
3273
|
-
let timeout;
|
|
3274
|
-
let timedOut = false;
|
|
3275
|
-
let resolveAbort;
|
|
3276
|
-
const abortFromRun = () => {
|
|
3277
|
-
const message = "OpenClaw dynamic tool call aborted.";
|
|
3278
|
-
params.onFallbackSelected?.();
|
|
3279
|
-
controller.abort(params.signal.reason ?? /* @__PURE__ */ new Error(message));
|
|
3280
|
-
notifyFailedToolResult(message);
|
|
3281
|
-
resolveAbort?.(failedDynamicToolResponse(message, { sideEffectEvidence: true }));
|
|
3282
|
-
};
|
|
3283
|
-
const abortPromise = new Promise((resolve) => {
|
|
3284
|
-
resolveAbort = resolve;
|
|
3285
|
-
});
|
|
3286
|
-
const timeoutPromise = new Promise((resolve) => {
|
|
3287
|
-
const timeoutMs = clampDynamicToolTimeoutMs(params.timeoutMs);
|
|
3288
|
-
timeout = setTimeout(() => {
|
|
3289
|
-
timedOut = true;
|
|
3290
|
-
const timeoutDetails = formatDynamicToolTimeoutDetails({
|
|
3291
|
-
call: params.call,
|
|
3292
|
-
timeoutMs
|
|
3293
|
-
});
|
|
3294
|
-
params.onFallbackSelected?.();
|
|
3295
|
-
controller.abort(new Error(timeoutDetails.responseMessage));
|
|
3296
|
-
params.onTimeout?.();
|
|
3297
|
-
embeddedAgentLog.warn("codex dynamic tool call timed out", {
|
|
3298
|
-
...timeoutDetails.meta,
|
|
3299
|
-
consoleMessage: timeoutDetails.consoleMessage
|
|
3300
|
-
});
|
|
3301
|
-
notifyFailedToolResult(timeoutDetails.responseMessage);
|
|
3302
|
-
resolve(failedDynamicToolResponse(timeoutDetails.responseMessage, { sideEffectEvidence: true }));
|
|
3303
|
-
}, timeoutMs);
|
|
3304
|
-
timeout.unref?.();
|
|
3305
|
-
});
|
|
3306
|
-
try {
|
|
3307
|
-
params.signal.addEventListener("abort", abortFromRun, { once: true });
|
|
3308
|
-
if (params.signal.aborted) abortFromRun();
|
|
3309
|
-
const response = await Promise.race([
|
|
3310
|
-
params.toolBridge.handleToolCall(params.call, {
|
|
3311
|
-
signal: controller.signal,
|
|
3312
|
-
onAgentToolResult: notifyAgentToolResult,
|
|
3313
|
-
toolCallOrdinal: params.toolCallOrdinal
|
|
3314
|
-
}),
|
|
3315
|
-
abortPromise,
|
|
3316
|
-
timeoutPromise
|
|
3317
|
-
]);
|
|
3318
|
-
if (!response.success && !didNotifyAgentToolResult) notifyFailedToolResult(readDynamicToolResponseText(response));
|
|
3319
|
-
return response;
|
|
3320
|
-
} catch (error) {
|
|
3321
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
3322
|
-
notifyFailedToolResult(message);
|
|
3323
|
-
return failedDynamicToolResponse(message, { sideEffectEvidence: true });
|
|
3324
|
-
} finally {
|
|
3325
|
-
if (timeout) clearTimeout(timeout);
|
|
3326
|
-
params.signal.removeEventListener("abort", abortFromRun);
|
|
3327
|
-
resolveAbort = void 0;
|
|
3328
|
-
if (!timedOut && !controller.signal.aborted) controller.abort(/* @__PURE__ */ new Error("OpenClaw dynamic tool call finished."));
|
|
3329
|
-
}
|
|
3330
|
-
}
|
|
3331
|
-
function readDynamicToolResponseText(response) {
|
|
3332
|
-
return response.contentItems.flatMap((item) => item.type === "inputText" && typeof item.text === "string" ? [item.text] : []).join("\n").trim() || "OpenClaw dynamic tool call failed.";
|
|
3333
|
-
}
|
|
3334
|
-
function failedDynamicToolResponse(message, options) {
|
|
3335
|
-
const response = {
|
|
3336
|
-
contentItems: [{
|
|
3337
|
-
type: "inputText",
|
|
3338
|
-
text: message
|
|
3339
|
-
}],
|
|
3340
|
-
success: false
|
|
3341
|
-
};
|
|
3342
|
-
Object.defineProperty(response, "diagnosticTerminalType", {
|
|
3343
|
-
configurable: true,
|
|
3344
|
-
enumerable: false,
|
|
3345
|
-
value: "error"
|
|
3346
|
-
});
|
|
3347
|
-
if (options?.sideEffectEvidence === true) Object.defineProperty(response, "sideEffectEvidence", {
|
|
3348
|
-
configurable: true,
|
|
3349
|
-
enumerable: false,
|
|
3350
|
-
value: true
|
|
3351
|
-
});
|
|
3352
|
-
return response;
|
|
3353
|
-
}
|
|
3354
|
-
/** Strips OpenClaw-only metadata before sending a dynamic tool response to Codex. */
|
|
3355
|
-
function toCodexDynamicToolProtocolResponse(response) {
|
|
3356
|
-
return {
|
|
3357
|
-
contentItems: response.contentItems,
|
|
3358
|
-
success: response.success
|
|
3359
|
-
};
|
|
3360
|
-
}
|
|
3361
|
-
/** Adds async-started progress details when a tool result continues out of band. */
|
|
3362
|
-
function toCodexDynamicToolProgressResponse(response, protocolResponse) {
|
|
3363
|
-
if (response.asyncStarted !== true) return protocolResponse;
|
|
3364
|
-
return {
|
|
3365
|
-
...protocolResponse,
|
|
3366
|
-
details: {
|
|
3367
|
-
async: true,
|
|
3368
|
-
status: "started"
|
|
3369
|
-
}
|
|
3370
|
-
};
|
|
3371
|
-
}
|
|
3372
|
-
/** Decides whether a terminal dynamic tool response can release the Codex turn. */
|
|
3373
|
-
function shouldReleaseTurnAfterTerminalDynamicTool(state) {
|
|
3374
|
-
return !state.completed && !state.aborted && state.responseSuccess && !state.currentTurnHadNonTerminalDynamicToolResult && state.activeAppServerTurnRequests === 0 && state.activeTurnItemIdsCount === 0 && state.pendingOpenClawDynamicToolCompletionIdsCount === 0;
|
|
3375
|
-
}
|
|
3376
|
-
/** Returns true when a non-async result should block terminal-release shortcuts. */
|
|
3377
|
-
function shouldBlockTerminalReleaseForNonTerminalDynamicToolResult(response) {
|
|
3378
|
-
return response.asyncStarted !== true;
|
|
3379
|
-
}
|
|
3380
|
-
/** Resolves whether terminal diagnostic state should release, wait, or stay idle. */
|
|
3381
|
-
function resolveTerminalDynamicToolBatchAction(state) {
|
|
3382
|
-
if (state.activeAppServerTurnRequests > 0 || state.activeTurnItemIdsCount > 0 || state.pendingOpenClawDynamicToolCompletionIdsCount > 0) return "wait";
|
|
3383
|
-
if (state.currentTurnHadNonTerminalDynamicToolResult) return "clear-nonterminal-batch";
|
|
3384
|
-
if (state.hasPendingTerminalDynamicToolRelease) return "release-pending-terminal";
|
|
3385
|
-
return "idle";
|
|
3386
|
-
}
|
|
3387
|
-
/** Returns true for diagnostic events that terminate a dynamic tool call. */
|
|
3388
|
-
function isDynamicToolTerminalDiagnosticEvent(event) {
|
|
3389
|
-
return event.type === "tool.execution.completed" || event.type === "tool.execution.error" || event.type === "tool.execution.blocked";
|
|
3390
|
-
}
|
|
3391
|
-
/** Matches terminal diagnostics to a specific dynamic tool call id/name. */
|
|
3392
|
-
function isMatchingDynamicToolTerminalDiagnostic(params) {
|
|
3393
|
-
if (params.event.toolCallId !== params.call.callId || params.event.toolName !== params.call.tool) return false;
|
|
3394
|
-
if (params.runId !== void 0) return params.event.runId === params.runId;
|
|
3395
|
-
if (params.sessionId !== void 0) return params.event.sessionId === params.sessionId;
|
|
3396
|
-
if (params.sessionKey !== void 0) return params.event.sessionKey === params.sessionKey;
|
|
3397
|
-
return params.event.runId === void 0 && params.event.sessionId === void 0 && params.event.sessionKey === void 0;
|
|
3398
|
-
}
|
|
3399
|
-
/** Checks pending diagnostics for a terminal event matching a tool call. */
|
|
3400
|
-
function hasPendingDynamicToolTerminalDiagnostic(params) {
|
|
3401
|
-
return hasPendingInternalDiagnosticEvent((event) => {
|
|
3402
|
-
if (!isDynamicToolTerminalDiagnosticEvent(event)) return false;
|
|
3403
|
-
return isMatchingDynamicToolTerminalDiagnostic({
|
|
3404
|
-
event,
|
|
3405
|
-
call: params.call,
|
|
3406
|
-
runId: params.runId,
|
|
3407
|
-
sessionId: params.sessionId,
|
|
3408
|
-
sessionKey: params.sessionKey
|
|
3409
|
-
});
|
|
3410
|
-
});
|
|
3411
|
-
}
|
|
3412
|
-
/** Resolves per-tool timeout, applying media/message defaults and hard caps. */
|
|
3413
|
-
function resolveDynamicToolCallTimeoutMs(params) {
|
|
3414
|
-
return clampDynamicToolTimeoutMs(readDynamicToolCallTimeoutMs(params.call.arguments) ?? readConfiguredDynamicToolTimeoutMs(params.call.tool, params.config) ?? 9e4);
|
|
3415
|
-
}
|
|
3416
|
-
function readDynamicToolCallTimeoutMs(value) {
|
|
3417
|
-
if (!isJsonObject(value)) return;
|
|
3418
|
-
return readPositiveFiniteTimeoutMs(value.timeoutMs);
|
|
3419
|
-
}
|
|
3420
|
-
function readConfiguredDynamicToolTimeoutMs(toolName, config) {
|
|
3421
|
-
if (toolName === "image_generate") {
|
|
3422
|
-
const imageGenerationModel = config?.agents?.defaults?.imageGenerationModel;
|
|
3423
|
-
if (!imageGenerationModel || typeof imageGenerationModel !== "object") return CODEX_DYNAMIC_IMAGE_GENERATION_TOOL_TIMEOUT_MS;
|
|
3424
|
-
return readPositiveFiniteTimeoutMs(imageGenerationModel.timeoutMs) ?? CODEX_DYNAMIC_IMAGE_GENERATION_TOOL_TIMEOUT_MS;
|
|
3425
|
-
}
|
|
3426
|
-
if (toolName === "image") return readTimeoutSecondsAsMs(config?.tools?.media?.image?.timeoutSeconds) ?? 6e4;
|
|
3427
|
-
if (toolName === "message") return CODEX_DYNAMIC_MESSAGE_TOOL_TIMEOUT_MS;
|
|
3428
|
-
}
|
|
3429
|
-
function readTimeoutSecondsAsMs(value) {
|
|
3430
|
-
const seconds = readPositiveFiniteTimeoutMs(value);
|
|
3431
|
-
return seconds === void 0 ? void 0 : seconds * 1e3;
|
|
3432
|
-
}
|
|
3433
|
-
function readPositiveFiniteTimeoutMs(value) {
|
|
3434
|
-
return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : void 0;
|
|
3435
|
-
}
|
|
3436
|
-
function clampDynamicToolTimeoutMs(timeoutMs) {
|
|
3437
|
-
return Math.max(1, Math.min(CODEX_DYNAMIC_TOOL_MAX_TIMEOUT_MS, Math.floor(timeoutMs)));
|
|
3438
|
-
}
|
|
3439
|
-
//#endregion
|
|
3440
|
-
//#region extensions/codex/src/app-server/local-runtime-attribution.ts
|
|
3441
|
-
const OPENAI_PROVIDER_ID = "openai";
|
|
3442
|
-
const OPENAI_RESPONSES_API = "openai-responses";
|
|
3443
|
-
const OPENAI_CODEX_RESPONSES_API = "openai-chatgpt-responses";
|
|
3444
|
-
function normalizeRuntimeId(value) {
|
|
3445
|
-
return value?.trim().toLowerCase() ?? "";
|
|
3446
|
-
}
|
|
3447
|
-
/** Maps local Codex runtime plans onto the provider/api pair exposed to event projection. */
|
|
3448
|
-
function resolveCodexLocalRuntimeAttribution(params) {
|
|
3449
|
-
const authProfileProvider = normalizeRuntimeId(params.runtimePlan?.auth?.authProfileProviderForAuth);
|
|
3450
|
-
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 {
|
|
3451
|
-
provider: OPENAI_PROVIDER_ID,
|
|
3452
|
-
api: OPENAI_CODEX_RESPONSES_API
|
|
3453
|
-
};
|
|
3454
|
-
return {
|
|
3455
|
-
provider: params.provider,
|
|
3456
|
-
api: params.model.api
|
|
3457
|
-
};
|
|
3458
|
-
}
|
|
3459
|
-
//#endregion
|
|
3460
|
-
//#region extensions/codex/src/app-server/tool-progress-normalization.ts
|
|
3461
|
-
/**
|
|
3462
|
-
* Normalizes and sanitizes Codex dynamic-tool progress payloads before they are
|
|
3463
|
-
* emitted into OpenClaw events or logs.
|
|
3464
|
-
*/
|
|
3465
|
-
/** Maps OpenClaw tool-progress config to the mode used by Codex progress metadata. */
|
|
3466
|
-
function resolveCodexToolProgressDetailMode(value) {
|
|
3467
|
-
return value === "raw" ? "raw" : "explain";
|
|
3468
|
-
}
|
|
3469
|
-
/** Recursively redacts sensitive strings and handles circular values in event payloads. */
|
|
3470
|
-
function sanitizeCodexAgentEventValue(value, seen = /* @__PURE__ */ new WeakSet()) {
|
|
3471
|
-
if (typeof value === "string") return redactToolPayloadText(value);
|
|
3472
|
-
if (Array.isArray(value)) {
|
|
3473
|
-
if (seen.has(value)) return "[Circular]";
|
|
3474
|
-
seen.add(value);
|
|
3475
|
-
return value.map((entry) => sanitizeCodexAgentEventValue(entry, seen));
|
|
3476
|
-
}
|
|
3477
|
-
if (value && typeof value === "object") {
|
|
3478
|
-
if (seen.has(value)) return "[Circular]";
|
|
3479
|
-
seen.add(value);
|
|
3480
|
-
const out = {};
|
|
3481
|
-
for (const [key, child] of Object.entries(value)) out[key] = typeof child === "string" ? redactSensitiveFieldValue(key, child) : sanitizeCodexAgentEventValue(child, seen);
|
|
3482
|
-
return out;
|
|
3483
|
-
}
|
|
3484
|
-
return value;
|
|
3485
|
-
}
|
|
3486
|
-
/** Sanitizes a record-shaped Codex agent event payload. */
|
|
3487
|
-
function sanitizeCodexAgentEventRecord(value) {
|
|
3488
|
-
return sanitizeCodexAgentEventValue(value);
|
|
3489
|
-
}
|
|
3490
|
-
/** Sanitizes dynamic-tool arguments before diagnostic/event emission. */
|
|
3491
|
-
function sanitizeCodexToolArguments(value) {
|
|
3492
|
-
if (!isJsonObject(value)) return;
|
|
3493
|
-
return sanitizeCodexAgentEventRecord(value);
|
|
3494
|
-
}
|
|
3495
|
-
/** Sanitizes a Codex dynamic-tool response before diagnostic/event emission. */
|
|
3496
|
-
function sanitizeCodexToolResponse(response) {
|
|
3497
|
-
return sanitizeCodexAgentEventRecord(response);
|
|
3498
|
-
}
|
|
3499
|
-
/** Infers compact human-readable tool metadata from Codex dynamic-tool arguments. */
|
|
3500
|
-
function inferCodexDynamicToolMeta(call, detailMode) {
|
|
3501
|
-
return inferToolMetaFromArgs(call.tool, call.arguments, { detailMode });
|
|
3502
|
-
}
|
|
3503
|
-
//#endregion
|
|
3504
|
-
//#region extensions/codex/src/app-server/transcript-mirror.ts
|
|
3505
|
-
const MIRROR_IDENTITY_META_KEY = "mirrorIdentity";
|
|
3506
|
-
function buildSenderLabel(params) {
|
|
3507
|
-
const label = params.senderName ?? params.senderUsername ?? params.senderE164 ?? params.senderId;
|
|
3508
|
-
if (!label) return;
|
|
3509
|
-
if (!params.senderId || label.includes(params.senderId)) return label;
|
|
3510
|
-
return `${label} (${params.senderId})`;
|
|
3511
|
-
}
|
|
3512
|
-
function buildCodexUserPromptMessageFromPrepared(params, preparedUserMessage) {
|
|
3513
|
-
const senderId = normalizeOptionalString(params.senderId);
|
|
3514
|
-
const senderName = normalizeOptionalString(params.senderName);
|
|
3515
|
-
const senderUsername = normalizeOptionalString(params.senderUsername);
|
|
3516
|
-
const senderE164 = normalizeOptionalString(params.senderE164);
|
|
3517
|
-
const senderLabel = buildSenderLabel({
|
|
3518
|
-
senderId,
|
|
3519
|
-
senderName,
|
|
3520
|
-
senderUsername,
|
|
3521
|
-
senderE164
|
|
3522
|
-
});
|
|
3523
|
-
const sourceChannel = normalizeOptionalString(params.inputProvenance?.sourceChannel ?? params.messageChannel ?? params.messageProvider);
|
|
3524
|
-
if (preparedUserMessage) return {
|
|
3525
|
-
role: "user",
|
|
3526
|
-
timestamp: Date.now(),
|
|
3527
|
-
...params.inputProvenance ? { provenance: params.inputProvenance } : {},
|
|
3528
|
-
...sourceChannel ? { sourceChannel } : {},
|
|
3529
|
-
...senderId ? { senderId } : {},
|
|
3530
|
-
...senderName ? { senderName } : {},
|
|
3531
|
-
...senderUsername ? { senderUsername } : {},
|
|
3532
|
-
...senderE164 ? { senderE164 } : {},
|
|
3533
|
-
...senderLabel ? { senderLabel } : {},
|
|
3534
|
-
...preparedUserMessage
|
|
3535
|
-
};
|
|
3536
|
-
return {
|
|
3537
|
-
role: "user",
|
|
3538
|
-
content: params.prompt,
|
|
3539
|
-
timestamp: Date.now(),
|
|
3540
|
-
...params.inputProvenance ? { provenance: params.inputProvenance } : {},
|
|
3541
|
-
...sourceChannel ? { sourceChannel } : {},
|
|
3542
|
-
...senderId ? { senderId } : {},
|
|
3543
|
-
...senderName ? { senderName } : {},
|
|
3544
|
-
...senderUsername ? { senderUsername } : {},
|
|
3545
|
-
...senderE164 ? { senderE164 } : {},
|
|
3546
|
-
...senderLabel ? { senderLabel } : {}
|
|
3547
|
-
};
|
|
3548
|
-
}
|
|
3549
|
-
function buildCodexUserPromptMessage(params) {
|
|
3550
|
-
return buildCodexUserPromptMessageFromPrepared(params, params.userTurnTranscriptRecorder?.message);
|
|
3551
|
-
}
|
|
3552
|
-
async function buildResolvedCodexUserPromptMessage(params) {
|
|
3553
|
-
return buildCodexUserPromptMessageFromPrepared(params, await params.userTurnTranscriptRecorder?.resolveMessage() ?? params.userTurnTranscriptRecorder?.message);
|
|
3554
|
-
}
|
|
3555
|
-
async function mirrorTranscriptBestEffort(params) {
|
|
3556
|
-
try {
|
|
3557
|
-
const messages = await resolveFinalCodexMirrorMessages({
|
|
3558
|
-
params: params.params,
|
|
3559
|
-
messagesSnapshot: params.result.messagesSnapshot,
|
|
3560
|
-
turnId: params.turnId
|
|
3561
|
-
});
|
|
3562
|
-
const mirrorResult = await mirrorCodexAppServerTranscript({
|
|
3563
|
-
sessionFile: params.params.sessionFile,
|
|
3564
|
-
agentId: params.agentId,
|
|
3565
|
-
sessionKey: params.sessionKey,
|
|
3566
|
-
sessionId: params.params.sessionId,
|
|
3567
|
-
cwd: params.cwd,
|
|
3568
|
-
messages,
|
|
3569
|
-
idempotencyScope: `codex-app-server:${params.threadId}`,
|
|
3570
|
-
config: params.params.config
|
|
3571
|
-
});
|
|
3572
|
-
for (const message of mirrorResult.userMessagesPresent) params.notifyUserMessagePersisted(message);
|
|
3573
|
-
} catch (error) {
|
|
3574
|
-
embeddedAgentLog.warn("failed to mirror codex app-server transcript", { error });
|
|
3575
|
-
}
|
|
3576
|
-
}
|
|
3577
|
-
async function resolveFinalCodexMirrorMessages(params) {
|
|
3578
|
-
if (params.params.suppressNextUserMessagePersistence || !params.params.userTurnTranscriptRecorder) return params.messagesSnapshot;
|
|
3579
|
-
const resolvedPrompt = attachCodexMirrorIdentity(await buildResolvedCodexUserPromptMessage(params.params), `${params.turnId}:prompt`);
|
|
3580
|
-
const firstUserIndex = params.messagesSnapshot.findIndex((message) => message.role === "user");
|
|
3581
|
-
if (firstUserIndex === -1) return [resolvedPrompt, ...params.messagesSnapshot];
|
|
3582
|
-
const messages = params.messagesSnapshot.slice();
|
|
3583
|
-
messages[firstUserIndex] = resolvedPrompt;
|
|
3584
|
-
return messages;
|
|
3585
|
-
}
|
|
3586
|
-
function createCodexAppServerUserMessagePersistenceNotifier(runParams) {
|
|
3587
|
-
let notified = false;
|
|
3588
|
-
return (message) => {
|
|
3589
|
-
if (notified) return;
|
|
3590
|
-
notified = true;
|
|
3591
|
-
runParams.userTurnTranscriptRecorder?.markRuntimePersisted(message);
|
|
3592
|
-
try {
|
|
3593
|
-
runParams.onUserMessagePersisted?.(message);
|
|
3594
|
-
} catch (error) {
|
|
3595
|
-
embeddedAgentLog.warn("codex app-server user persistence notification failed", { error: formatErrorMessage(error) });
|
|
3596
|
-
}
|
|
3597
|
-
};
|
|
3598
|
-
}
|
|
3599
|
-
async function mirrorPromptAtTurnStartBestEffort(params) {
|
|
3600
|
-
if (params.params.suppressNextUserMessagePersistence) return;
|
|
3601
|
-
try {
|
|
3602
|
-
const mirrorPromise = (async () => {
|
|
3603
|
-
const userPromptMessage = attachCodexMirrorIdentity(await buildResolvedCodexUserPromptMessage(params.params), `${params.turnId}:prompt`);
|
|
3604
|
-
const mirrorResult = await mirrorCodexAppServerTranscript({
|
|
3605
|
-
sessionFile: params.params.sessionFile,
|
|
3606
|
-
agentId: params.agentId,
|
|
3607
|
-
sessionKey: params.sessionKey,
|
|
3608
|
-
sessionId: params.params.sessionId,
|
|
3609
|
-
cwd: params.cwd,
|
|
3610
|
-
messages: [userPromptMessage],
|
|
3611
|
-
idempotencyScope: `codex-app-server:${params.threadId}`,
|
|
3612
|
-
config: params.params.config
|
|
3613
|
-
});
|
|
3614
|
-
for (const message of mirrorResult.userMessagesPresent) params.notifyUserMessagePersisted(message);
|
|
3615
|
-
})();
|
|
3616
|
-
params.params.userTurnTranscriptRecorder?.markRuntimePersistencePending(mirrorPromise);
|
|
3617
|
-
await mirrorPromise;
|
|
3618
|
-
} catch (error) {
|
|
3619
|
-
embeddedAgentLog.warn("failed to mirror codex app-server prompt at turn start", { error });
|
|
3620
|
-
}
|
|
3621
|
-
}
|
|
3622
|
-
/**
|
|
3623
|
-
* Tag a message with a stable logical identity for mirror dedupe. Callers
|
|
3624
|
-
* should use a value that is invariant for the same logical message across
|
|
3625
|
-
* re-emits (e.g. `${turnId}:prompt`, `${turnId}:assistant`) but distinct
|
|
3626
|
-
* for genuinely-distinct messages (different turns, different kinds). When
|
|
3627
|
-
* present this identity replaces the role/content fingerprint in the
|
|
3628
|
-
* idempotency key, so the dedupe survives caller-scope rotation without
|
|
3629
|
-
* collapsing distinct same-content turns.
|
|
3630
|
-
*/
|
|
3631
|
-
function attachCodexMirrorIdentity(message, identity) {
|
|
3632
|
-
const record = message;
|
|
3633
|
-
const existing = record["__openclaw"];
|
|
3634
|
-
const baseMeta = existing && typeof existing === "object" && !Array.isArray(existing) ? existing : {};
|
|
3635
|
-
return {
|
|
3636
|
-
...record,
|
|
3637
|
-
__openclaw: {
|
|
3638
|
-
...baseMeta,
|
|
3639
|
-
[MIRROR_IDENTITY_META_KEY]: identity
|
|
3640
|
-
}
|
|
3641
|
-
};
|
|
3642
|
-
}
|
|
3643
|
-
function readMirrorIdentity(message) {
|
|
3644
|
-
const meta = message["__openclaw"];
|
|
3645
|
-
if (!meta || typeof meta !== "object" || Array.isArray(meta)) return;
|
|
3646
|
-
const id = meta[MIRROR_IDENTITY_META_KEY];
|
|
3647
|
-
return typeof id === "string" && id.length > 0 ? id : void 0;
|
|
3648
|
-
}
|
|
3649
|
-
function fingerprintMirrorMessageContent(message) {
|
|
3650
|
-
const payload = JSON.stringify({
|
|
3651
|
-
role: message.role,
|
|
3652
|
-
content: message.content
|
|
3653
|
-
});
|
|
3654
|
-
return createHash("sha256").update(payload).digest("hex").slice(0, 16);
|
|
3655
|
-
}
|
|
3656
|
-
function buildMirrorDedupeIdentity(message) {
|
|
3657
|
-
const explicit = readMirrorIdentity(message);
|
|
3658
|
-
if (explicit) return explicit;
|
|
3659
|
-
return `${message.role}:${fingerprintMirrorMessageContent(message)}`;
|
|
3660
|
-
}
|
|
3661
|
-
async function mirrorCodexAppServerTranscript(params) {
|
|
3662
|
-
const messages = params.messages.filter((message) => message.role === "user" || message.role === "assistant" || message.role === "toolResult");
|
|
3663
|
-
if (messages.length === 0) return { userMessagesPresent: [] };
|
|
3664
|
-
const transcriptTarget = resolveCodexMirrorTranscriptTarget(params);
|
|
3665
|
-
const { appendedUpdates, userMessagesPresent } = await withSessionTranscriptWriteLock({
|
|
3666
|
-
...transcriptTarget,
|
|
3667
|
-
config: params.config
|
|
3668
|
-
}, async (transcript) => {
|
|
3669
|
-
const nextAppendedUpdates = [];
|
|
3670
|
-
const nextUserMessagesPresent = [];
|
|
3671
|
-
const mirrorState = readTranscriptMirrorState(await transcript.readEvents());
|
|
3672
|
-
let nextMessageSeq = mirrorState.messageCount;
|
|
3673
|
-
for (const message of messages) {
|
|
3674
|
-
const dedupeIdentity = buildMirrorDedupeIdentity(message);
|
|
3675
|
-
const idempotencyKey = params.idempotencyScope ? `${params.idempotencyScope}:${dedupeIdentity}` : void 0;
|
|
3676
|
-
const transcriptMessage = {
|
|
3677
|
-
...message,
|
|
3678
|
-
...idempotencyKey ? { idempotencyKey } : {}
|
|
3679
|
-
};
|
|
3680
|
-
if (idempotencyKey && mirrorState.idempotencyKeys.has(idempotencyKey)) {
|
|
3681
|
-
const persistedUserMessage = mirrorState.userMessagesByIdempotencyKey.get(idempotencyKey);
|
|
3682
|
-
if (persistedUserMessage) nextUserMessagesPresent.push(persistedUserMessage);
|
|
3683
|
-
continue;
|
|
3684
|
-
}
|
|
3685
|
-
const nextMessage = runAgentHarnessBeforeMessageWriteHook({
|
|
3686
|
-
message: transcriptMessage,
|
|
3687
|
-
agentId: params.agentId,
|
|
3688
|
-
sessionKey: params.sessionKey
|
|
3689
|
-
});
|
|
3690
|
-
if (!nextMessage) continue;
|
|
3691
|
-
const messageToAppend = idempotencyKey ? {
|
|
3692
|
-
...nextMessage,
|
|
3693
|
-
idempotencyKey
|
|
3694
|
-
} : nextMessage;
|
|
3695
|
-
const appended = await transcript.appendMessage({
|
|
3696
|
-
message: messageToAppend,
|
|
3697
|
-
idempotencyLookup: idempotencyKey ? "caller-checked" : "scan",
|
|
3698
|
-
cwd: params.cwd
|
|
3699
|
-
});
|
|
3700
|
-
if (!appended) continue;
|
|
3701
|
-
const { messageId, message: appendedMessage } = appended;
|
|
3702
|
-
if (appendedMessage.role === "user") {
|
|
3703
|
-
nextUserMessagesPresent.push(appendedMessage);
|
|
3704
|
-
if (idempotencyKey) mirrorState.userMessagesByIdempotencyKey.set(idempotencyKey, appendedMessage);
|
|
3705
|
-
}
|
|
3706
|
-
nextMessageSeq += 1;
|
|
3707
|
-
nextAppendedUpdates.push({
|
|
3708
|
-
messageId,
|
|
3709
|
-
message: appendedMessage,
|
|
3710
|
-
messageSeq: nextMessageSeq
|
|
3711
|
-
});
|
|
3712
|
-
if (idempotencyKey) mirrorState.idempotencyKeys.add(idempotencyKey);
|
|
3713
|
-
}
|
|
3714
|
-
return {
|
|
3715
|
-
appendedUpdates: nextAppendedUpdates,
|
|
3716
|
-
userMessagesPresent: nextUserMessagesPresent
|
|
3717
|
-
};
|
|
3718
|
-
});
|
|
3719
|
-
for (const update of appendedUpdates) await publishSessionTranscriptUpdateByIdentity({
|
|
3720
|
-
...transcriptTarget,
|
|
3721
|
-
update: {
|
|
3722
|
-
...params.sessionKey ? { sessionKey: params.sessionKey } : {},
|
|
3723
|
-
...params.agentId ? { agentId: params.agentId } : {},
|
|
3724
|
-
message: update.message,
|
|
3725
|
-
messageId: update.messageId,
|
|
3726
|
-
messageSeq: update.messageSeq
|
|
3727
|
-
}
|
|
3728
|
-
});
|
|
3729
|
-
return { userMessagesPresent };
|
|
3730
|
-
}
|
|
3731
|
-
function resolveCodexMirrorTranscriptTarget(params) {
|
|
3732
|
-
return {
|
|
3733
|
-
...params.agentId ? { agentId: params.agentId } : {},
|
|
3734
|
-
sessionFile: params.sessionFile,
|
|
3735
|
-
sessionId: params.sessionId,
|
|
3736
|
-
sessionKey: params.sessionKey ?? ""
|
|
3737
|
-
};
|
|
3738
|
-
}
|
|
3739
|
-
function readTranscriptMirrorState(events) {
|
|
3740
|
-
const idempotencyKeys = /* @__PURE__ */ new Set();
|
|
3741
|
-
const userMessagesByIdempotencyKey = /* @__PURE__ */ new Map();
|
|
3742
|
-
let messageCount = 0;
|
|
3743
|
-
for (const event of events) {
|
|
3744
|
-
if (!event || typeof event !== "object" || Array.isArray(event)) continue;
|
|
3745
|
-
const parsed = event;
|
|
3746
|
-
if (parsed.type === "message") messageCount += 1;
|
|
3747
|
-
if (typeof parsed.message?.idempotencyKey === "string") {
|
|
3748
|
-
idempotencyKeys.add(parsed.message.idempotencyKey);
|
|
3749
|
-
if (parsed.message.role === "user") userMessagesByIdempotencyKey.set(parsed.message.idempotencyKey, parsed.message);
|
|
3750
|
-
}
|
|
3751
|
-
}
|
|
3752
|
-
return {
|
|
3753
|
-
idempotencyKeys,
|
|
3754
|
-
messageCount,
|
|
3755
|
-
userMessagesByIdempotencyKey
|
|
3756
|
-
};
|
|
3757
|
-
}
|
|
3758
|
-
//#endregion
|
|
3759
|
-
//#region extensions/codex/src/app-server/event-projector.ts
|
|
3760
|
-
const ZERO_USAGE = {
|
|
3761
|
-
input: 0,
|
|
3762
|
-
output: 0,
|
|
3763
|
-
cacheRead: 0,
|
|
3764
|
-
cacheWrite: 0,
|
|
3765
|
-
totalTokens: 0,
|
|
3766
|
-
cost: {
|
|
3767
|
-
input: 0,
|
|
3768
|
-
output: 0,
|
|
3769
|
-
cacheRead: 0,
|
|
3770
|
-
cacheWrite: 0,
|
|
3771
|
-
total: 0
|
|
3772
|
-
}
|
|
3773
|
-
};
|
|
3774
|
-
const CURRENT_TOKEN_USAGE_KEYS = [
|
|
3775
|
-
"last",
|
|
3776
|
-
"current",
|
|
3777
|
-
"lastCall",
|
|
3778
|
-
"lastCallUsage",
|
|
3779
|
-
"lastTokenUsage",
|
|
3780
|
-
"last_token_usage"
|
|
3781
|
-
];
|
|
3782
|
-
const CODEX_PROMPT_TOTAL_INPUT_KEYS = [
|
|
3783
|
-
"inputTokens",
|
|
3784
|
-
"input_tokens",
|
|
3785
|
-
"promptTokens",
|
|
3786
|
-
"prompt_tokens"
|
|
3787
|
-
];
|
|
3788
|
-
const MAX_TOOL_OUTPUT_DELTA_MESSAGES_PER_ITEM = 20;
|
|
3789
|
-
const TOOL_TRANSCRIPT_OUTPUT_MAX_CHARS = 12e3;
|
|
3790
|
-
const MISSING_TOOL_RESULT_ERROR = "OpenClaw recorded a native Codex tool.call without a matching tool.result before the turn completed.";
|
|
3791
|
-
const GENERATED_IMAGE_MEDIA_SUBDIR = "tool-image-generation";
|
|
3792
|
-
const BYTES_PER_MB = 1024 * 1024;
|
|
3793
|
-
const DEFAULT_GENERATED_IMAGE_MAX_BYTES = 6 * BYTES_PER_MB;
|
|
3794
|
-
const TRANSCRIPT_PROGRESS_SUPPRESSED_TOOL_NAMES = new Set([
|
|
3795
|
-
"message",
|
|
3796
|
-
"messages",
|
|
3797
|
-
"reply",
|
|
3798
|
-
"send",
|
|
3799
|
-
"reaction",
|
|
3800
|
-
"react",
|
|
3801
|
-
"typing"
|
|
3802
|
-
]);
|
|
3803
|
-
function shouldEmitTranscriptToolProgress(toolName, _args) {
|
|
3804
|
-
const normalized = typeof toolName === "string" ? toolName.trim().toLowerCase() : "";
|
|
3805
|
-
return Boolean(normalized && !TRANSCRIPT_PROGRESS_SUPPRESSED_TOOL_NAMES.has(normalized));
|
|
3806
|
-
}
|
|
3807
|
-
var CodexAppServerEventProjector = class {
|
|
3808
|
-
constructor(params, threadId, turnId, options = {}) {
|
|
3809
|
-
this.params = params;
|
|
3810
|
-
this.threadId = threadId;
|
|
3811
|
-
this.turnId = turnId;
|
|
3812
|
-
this.options = options;
|
|
3813
|
-
this.assistantTextByItem = /* @__PURE__ */ new Map();
|
|
3814
|
-
this.assistantItemOrder = [];
|
|
3815
|
-
this.assistantPhaseByItem = /* @__PURE__ */ new Map();
|
|
3816
|
-
this.latestTerminalAssistantCandidateSuperseded = false;
|
|
3817
|
-
this.latestTerminalAssistantCandidateCanReleaseAfterToolHandoff = false;
|
|
3818
|
-
this.terminalAssistantCandidateEarlierActiveItemIds = /* @__PURE__ */ new Set();
|
|
3819
|
-
this.lastCommentaryProgressTextByItem = /* @__PURE__ */ new Map();
|
|
3820
|
-
this.pendingRawCommentaryEchoes = 0;
|
|
3821
|
-
this.reasoningTextByGroup = /* @__PURE__ */ new Map();
|
|
3822
|
-
this.reasoningItemOrder = /* @__PURE__ */ new Map();
|
|
3823
|
-
this.planTextByItem = /* @__PURE__ */ new Map();
|
|
3824
|
-
this.activeItemIds = /* @__PURE__ */ new Set();
|
|
3825
|
-
this.completedItemIds = /* @__PURE__ */ new Set();
|
|
3826
|
-
this.activeCompactionItemIds = /* @__PURE__ */ new Set();
|
|
3827
|
-
this.toolProgressTexts = /* @__PURE__ */ new Set();
|
|
3828
|
-
this.toolResultSummaryItemIds = /* @__PURE__ */ new Set();
|
|
3829
|
-
this.toolResultOutputItemIds = /* @__PURE__ */ new Set();
|
|
3830
|
-
this.toolResultOutputStreamedItemIds = /* @__PURE__ */ new Set();
|
|
3831
|
-
this.transcriptToolProgressSuppressedIds = /* @__PURE__ */ new Set();
|
|
3832
|
-
this.toolTranscriptArgumentsById = /* @__PURE__ */ new Map();
|
|
3833
|
-
this.toolResultOutputDeltaState = /* @__PURE__ */ new Map();
|
|
3834
|
-
this.toolResultOutputTextByItem = /* @__PURE__ */ new Map();
|
|
3835
|
-
this.toolMetas = /* @__PURE__ */ new Map();
|
|
3836
|
-
this.terminalPresentationClearedItemIds = /* @__PURE__ */ new Set();
|
|
3837
|
-
this.nativeToolOutcomeOrdinals = /* @__PURE__ */ new Map();
|
|
3838
|
-
this.sideEffectingToolItemIds = /* @__PURE__ */ new Set();
|
|
3839
|
-
this.sideEffectingDynamicToolCallIds = /* @__PURE__ */ new Set();
|
|
3840
|
-
this.toolTranscriptMessages = [];
|
|
3841
|
-
this.toolTranscriptCallIds = /* @__PURE__ */ new Set();
|
|
3842
|
-
this.toolTranscriptResultIds = /* @__PURE__ */ new Set();
|
|
3843
|
-
this.toolTranscriptNamesById = /* @__PURE__ */ new Map();
|
|
3844
|
-
this.toolTrajectoryCallIds = /* @__PURE__ */ new Set();
|
|
3845
|
-
this.toolTrajectoryResultIds = /* @__PURE__ */ new Set();
|
|
3846
|
-
this.toolTrajectoryNamesById = /* @__PURE__ */ new Map();
|
|
3847
|
-
this.toolTrajectoryItemsById = /* @__PURE__ */ new Map();
|
|
3848
|
-
this.transcriptToolProgressCallIds = /* @__PURE__ */ new Set();
|
|
3849
|
-
this.nativeGeneratedMediaItemIds = /* @__PURE__ */ new Set();
|
|
3850
|
-
this.nativeGeneratedMediaUrlsByItemId = /* @__PURE__ */ new Map();
|
|
3851
|
-
this.diagnosticToolStartedAtByItem = /* @__PURE__ */ new Map();
|
|
3852
|
-
this.afterToolCallObservedItemIds = /* @__PURE__ */ new Set();
|
|
3853
|
-
this.assistantStarted = false;
|
|
3854
|
-
this.reasoningStarted = false;
|
|
3855
|
-
this.reasoningEnded = false;
|
|
3856
|
-
this.streamedPartialAssistantItemReplaceable = false;
|
|
3857
|
-
this.promptErrorSource = null;
|
|
3858
|
-
this.synthesizedMissingToolResultError = null;
|
|
3859
|
-
this.aborted = false;
|
|
3860
|
-
this.guardianReviewCount = 0;
|
|
3861
|
-
this.completedCompactionCount = 0;
|
|
3862
|
-
}
|
|
3863
|
-
getCompletedTurnStatus() {
|
|
3864
|
-
return this.completedTurn?.status;
|
|
3865
|
-
}
|
|
3866
|
-
hasCompletedTerminalAssistantText() {
|
|
3867
|
-
const latestCompletedItemId = this.latestCompletedTerminalAssistantItemId;
|
|
3868
|
-
if (!latestCompletedItemId) return false;
|
|
3869
|
-
const finalItem = this.resolveFinalAssistantTextItem();
|
|
3870
|
-
return this.latestCompletedItemId === latestCompletedItemId && finalItem?.itemId === latestCompletedItemId && this.completedItemIds.has(latestCompletedItemId);
|
|
3871
|
-
}
|
|
3872
|
-
getLatestTerminalAssistantCandidate() {
|
|
3873
|
-
const itemId = this.latestTerminalAssistantCandidateItemId;
|
|
3874
|
-
if (!itemId) return;
|
|
3875
|
-
const text = this.assistantTextByItem.get(itemId)?.trim();
|
|
3876
|
-
return {
|
|
3877
|
-
itemId,
|
|
3878
|
-
hasText: Boolean(text && !this.toolProgressTexts.has(text))
|
|
3879
|
-
};
|
|
3880
|
-
}
|
|
3881
|
-
hasLatestTerminalAssistantCandidateText() {
|
|
3882
|
-
return !this.latestTerminalAssistantCandidateSuperseded && this.getLatestTerminalAssistantCandidate()?.hasText === true;
|
|
3883
|
-
}
|
|
3884
|
-
canReleaseLatestTerminalAssistantAfterToolHandoff() {
|
|
3885
|
-
return this.latestTerminalAssistantCandidateCanReleaseAfterToolHandoff && this.hasLatestTerminalAssistantCandidateText();
|
|
3886
|
-
}
|
|
3887
|
-
/** Restores a completed final item after only the enclosing turn timeout fired. */
|
|
3888
|
-
recoverCompletedTerminalAssistantAfterTurnWatchTimeout() {
|
|
3889
|
-
if (!this.aborted || this.promptError !== "codex app-server attempt timed out" || !this.hasCompletedTerminalAssistantText()) return false;
|
|
3890
|
-
this.aborted = false;
|
|
3891
|
-
this.promptError = void 0;
|
|
3892
|
-
this.promptErrorSource = null;
|
|
3893
|
-
return true;
|
|
3894
|
-
}
|
|
3895
|
-
/** Resolves the shared model-order position for a native tool item. */
|
|
3896
|
-
recordNativeToolOutcome(item) {
|
|
3897
|
-
if (!item || this.nativeToolOutcomeOrdinals.has(item.id) || !shouldClearTerminalPresentationForNativeItem(item)) return;
|
|
3898
|
-
const ordinal = this.params.allocateToolOutcomeOrdinal?.(item.id);
|
|
3899
|
-
if (ordinal !== void 0) this.nativeToolOutcomeOrdinals.set(item.id, ordinal);
|
|
3900
|
-
}
|
|
3901
|
-
async handleNotification(notification) {
|
|
3902
|
-
const params = isJsonObject(notification.params) ? notification.params : void 0;
|
|
3903
|
-
if (!params) return;
|
|
3904
|
-
if (notification.method === "account/rateLimits/updated") {
|
|
3905
|
-
this.latestRateLimits = params;
|
|
3906
|
-
rememberCodexRateLimits(params);
|
|
3907
|
-
return;
|
|
3908
|
-
}
|
|
3909
|
-
if (isHookNotificationMethod(notification.method)) {
|
|
3910
|
-
if (!this.isHookNotificationForCurrentThread(params)) return;
|
|
3911
|
-
} else if (!this.isNotificationForTurn(params)) return;
|
|
3912
|
-
switch (notification.method) {
|
|
3913
|
-
case "item/agentMessage/delta":
|
|
3914
|
-
await this.handleAssistantDelta(params);
|
|
3915
|
-
break;
|
|
3916
|
-
case "item/reasoning/summaryTextDelta":
|
|
3917
|
-
case "item/reasoning/textDelta":
|
|
3918
|
-
await this.handleReasoningDelta(notification.method, params);
|
|
3919
|
-
break;
|
|
3920
|
-
case "item/plan/delta":
|
|
3921
|
-
this.handlePlanDelta(params);
|
|
3922
|
-
break;
|
|
3923
|
-
case "turn/plan/updated":
|
|
3924
|
-
this.handleTurnPlanUpdated(params);
|
|
3925
|
-
break;
|
|
3926
|
-
case "item/started":
|
|
3927
|
-
await this.handleItemStarted(params);
|
|
3928
|
-
break;
|
|
3929
|
-
case "item/completed":
|
|
3930
|
-
await this.handleItemCompleted(params);
|
|
3931
|
-
break;
|
|
3932
|
-
case "item/commandExecution/outputDelta":
|
|
3933
|
-
this.handleOutputDelta(params, "bash");
|
|
3934
|
-
break;
|
|
3935
|
-
case "item/fileChange/outputDelta":
|
|
3936
|
-
this.handleOutputDelta(params, "apply_patch");
|
|
3937
|
-
break;
|
|
3938
|
-
case "item/autoApprovalReview/started":
|
|
3939
|
-
case "item/autoApprovalReview/completed":
|
|
3940
|
-
this.handleGuardianReviewNotification(notification.method, params);
|
|
3941
|
-
break;
|
|
3942
|
-
case "hook/started":
|
|
3943
|
-
case "hook/completed":
|
|
3944
|
-
this.handleHookNotification(notification.method, params);
|
|
3945
|
-
break;
|
|
3946
|
-
case "thread/tokenUsage/updated":
|
|
3947
|
-
this.handleTokenUsage(params);
|
|
3948
|
-
break;
|
|
3949
|
-
case "turn/completed":
|
|
3950
|
-
await this.handleTurnCompleted(params);
|
|
3951
|
-
break;
|
|
3952
|
-
case "rawResponseItem/completed":
|
|
3953
|
-
await this.handleRawResponseItemCompleted(params);
|
|
3954
|
-
break;
|
|
3955
|
-
case "error":
|
|
3956
|
-
if (readBooleanAlias(params, ["willRetry", "will_retry"]) === true) break;
|
|
3957
|
-
this.promptError = this.formatCodexErrorMessage(params) ?? "codex app-server error";
|
|
3958
|
-
this.promptErrorSource = "prompt";
|
|
3959
|
-
break;
|
|
3960
|
-
default: break;
|
|
3961
|
-
}
|
|
3962
|
-
}
|
|
3963
|
-
buildResult(toolTelemetry, options) {
|
|
3964
|
-
const assistantTexts = this.collectAssistantTexts();
|
|
3965
|
-
const reasoningText = collectReasoningTextValues(this.reasoningTextByGroup, this.reasoningItemOrder).join("\n\n");
|
|
3966
|
-
const planText = collectTextValues(this.planTextByItem).join("\n\n");
|
|
3967
|
-
const hasAssistantItemText = this.hasAssistantItemTextForSynthesis();
|
|
3968
|
-
const legacyFailClosed = !this.completedTurn || this.completedTurn.status !== "completed" || hasAssistantItemText;
|
|
3969
|
-
const hasDeliverableAssistantOnCompletedTurn = this.completedTurn?.status === "completed" && assistantTexts.some((text) => text.trim().length > 0);
|
|
3970
|
-
this.synthesizeMissingToolResults({
|
|
3971
|
-
synthesize: legacyFailClosed,
|
|
3972
|
-
recordPromptError: legacyFailClosed && !hasDeliverableAssistantOnCompletedTurn
|
|
3973
|
-
});
|
|
3974
|
-
const lastAssistant = assistantTexts.length > 0 ? this.createAssistantMessage(assistantTexts.join("\n\n")) : void 0;
|
|
3975
|
-
const currentAttemptAssistant = this.createCurrentAttemptAssistantMessage();
|
|
3976
|
-
const turnId = this.turnId;
|
|
3977
|
-
const messagesSnapshot = this.params.suppressNextUserMessagePersistence ? [] : [attachCodexMirrorIdentity(buildCodexUserPromptMessage(this.params), `${turnId}:prompt`)];
|
|
3978
|
-
if (reasoningText) messagesSnapshot.push(attachCodexMirrorIdentity(this.createAssistantMirrorMessage("Codex reasoning", reasoningText), `${turnId}:reasoning`));
|
|
3979
|
-
if (planText) messagesSnapshot.push(attachCodexMirrorIdentity(this.createAssistantMirrorMessage("Codex plan", planText), `${turnId}:plan`));
|
|
3980
|
-
messagesSnapshot.push(...this.toolTranscriptMessages);
|
|
3981
|
-
if (lastAssistant) messagesSnapshot.push(attachCodexMirrorIdentity(lastAssistant, `${turnId}:assistant`));
|
|
3982
|
-
const turnFailed = this.completedTurn?.status === "failed";
|
|
3983
|
-
const promptError = this.promptError ?? this.synthesizedMissingToolResultError ?? (turnFailed ? this.completedTurn?.error?.message ?? "codex app-server turn failed" : null);
|
|
3984
|
-
const agentHarnessResultClassification = classifyAgentHarnessTerminalOutcome({
|
|
3985
|
-
assistantTexts,
|
|
3986
|
-
reasoningText,
|
|
3987
|
-
planText,
|
|
3988
|
-
promptError,
|
|
3989
|
-
turnCompleted: Boolean(this.completedTurn)
|
|
3990
|
-
});
|
|
3991
|
-
const toolMetas = [...this.toolMetas.values()];
|
|
3992
|
-
const hadPotentialSideEffects = toolTelemetry.didSendViaMessagingTool || (toolTelemetry.successfulCronAdds ?? 0) > 0 || this.nativeGeneratedMediaItemIds.size > 0 || this.sideEffectingToolItemIds.size > 0 || this.sideEffectingDynamicToolCallIds.size > 0;
|
|
3993
|
-
return {
|
|
3994
|
-
aborted: this.aborted,
|
|
3995
|
-
externalAbort: false,
|
|
3996
|
-
timedOut: false,
|
|
3997
|
-
idleTimedOut: false,
|
|
3998
|
-
timedOutDuringCompaction: false,
|
|
3999
|
-
timedOutDuringToolExecution: false,
|
|
4000
|
-
promptError,
|
|
4001
|
-
promptErrorSource: promptError ? this.promptErrorSource || "prompt" : null,
|
|
4002
|
-
sessionIdUsed: this.params.sessionId,
|
|
4003
|
-
...agentHarnessResultClassification ? { agentHarnessResultClassification } : {},
|
|
4004
|
-
bootstrapPromptWarningSignaturesSeen: this.params.bootstrapPromptWarningSignaturesSeen,
|
|
4005
|
-
bootstrapPromptWarningSignature: this.params.bootstrapPromptWarningSignature,
|
|
4006
|
-
messagesSnapshot,
|
|
4007
|
-
assistantTexts,
|
|
4008
|
-
toolMetas,
|
|
4009
|
-
lastAssistant,
|
|
4010
|
-
currentAttemptAssistant,
|
|
4011
|
-
...this.lastNativeToolError ? { lastToolError: this.lastNativeToolError } : {},
|
|
4012
|
-
didSendViaMessagingTool: toolTelemetry.didSendViaMessagingTool,
|
|
4013
|
-
didDeliverSourceReplyViaMessageTool: toolTelemetry.didDeliverSourceReplyViaMessageTool === true,
|
|
4014
|
-
messagingToolSentTexts: toolTelemetry.messagingToolSentTexts,
|
|
4015
|
-
messagingToolSentMediaUrls: toolTelemetry.messagingToolSentMediaUrls,
|
|
4016
|
-
messagingToolSentTargets: toolTelemetry.messagingToolSentTargets,
|
|
4017
|
-
messagingToolSourceReplyPayloads: toolTelemetry.messagingToolSourceReplyPayloads ?? [],
|
|
4018
|
-
heartbeatToolResponse: toolTelemetry.heartbeatToolResponse,
|
|
4019
|
-
toolMediaUrls: this.buildToolMediaUrls(toolTelemetry),
|
|
4020
|
-
toolAudioAsVoice: toolTelemetry.toolAudioAsVoice,
|
|
4021
|
-
successfulCronAdds: toolTelemetry.successfulCronAdds,
|
|
4022
|
-
cloudCodeAssistFormatError: false,
|
|
4023
|
-
attemptUsage: this.tokenUsage,
|
|
4024
|
-
replayMetadata: {
|
|
4025
|
-
hadPotentialSideEffects,
|
|
4026
|
-
replaySafe: !hadPotentialSideEffects
|
|
4027
|
-
},
|
|
4028
|
-
itemLifecycle: {
|
|
4029
|
-
startedCount: this.activeItemIds.size + this.completedItemIds.size,
|
|
4030
|
-
completedCount: this.completedItemIds.size,
|
|
4031
|
-
activeCount: this.activeItemIds.size,
|
|
4032
|
-
...this.completedCompactionCount > 0 ? { compactionCount: this.completedCompactionCount } : {}
|
|
4033
|
-
},
|
|
4034
|
-
yieldDetected: options?.yieldDetected || false,
|
|
4035
|
-
didSendDeterministicApprovalPrompt: this.guardianReviewCount > 0 ? false : void 0
|
|
4036
|
-
};
|
|
4037
|
-
}
|
|
4038
|
-
recordDynamicToolCall(params) {
|
|
4039
|
-
const args = sanitizeCodexToolArguments(params.arguments);
|
|
4040
|
-
this.recordToolTranscriptCall({
|
|
4041
|
-
id: params.callId,
|
|
4042
|
-
name: params.tool,
|
|
4043
|
-
arguments: args
|
|
4044
|
-
});
|
|
4045
|
-
}
|
|
4046
|
-
recordDynamicToolResult(params) {
|
|
4047
|
-
const resultText = collectDynamicToolContentText(params.contentItems);
|
|
4048
|
-
if (params.asyncStarted === true) {
|
|
4049
|
-
const existing = this.toolMetas.get(params.callId);
|
|
4050
|
-
this.toolMetas.set(params.callId, {
|
|
4051
|
-
toolName: existing?.toolName ?? params.tool,
|
|
4052
|
-
...existing?.meta ? { meta: existing.meta } : {},
|
|
4053
|
-
asyncStarted: true
|
|
4054
|
-
});
|
|
4055
|
-
}
|
|
4056
|
-
this.recordToolTranscriptResult({
|
|
4057
|
-
id: params.callId,
|
|
4058
|
-
name: params.tool,
|
|
4059
|
-
text: resultText,
|
|
4060
|
-
isError: !params.success
|
|
4061
|
-
});
|
|
4062
|
-
if (!params.success && params.terminalType === "blocked") this.lastNativeToolError = {
|
|
4063
|
-
toolName: params.tool,
|
|
4064
|
-
error: resultText || "codex dynamic tool blocked"
|
|
4065
|
-
};
|
|
4066
|
-
else if (params.success && this.lastNativeToolError && !this.lastNativeToolError.mutatingAction) this.lastNativeToolError = void 0;
|
|
4067
|
-
if (params.sideEffectEvidence === true) this.sideEffectingDynamicToolCallIds.add(params.callId);
|
|
4068
|
-
}
|
|
4069
|
-
markTimedOut() {
|
|
4070
|
-
this.aborted = true;
|
|
4071
|
-
this.promptError = "codex app-server attempt timed out";
|
|
4072
|
-
this.promptErrorSource = "prompt";
|
|
4073
|
-
}
|
|
4074
|
-
markAborted() {
|
|
4075
|
-
this.aborted = true;
|
|
4076
|
-
}
|
|
4077
|
-
isCompacting() {
|
|
4078
|
-
return this.activeCompactionItemIds.size > 0;
|
|
4079
|
-
}
|
|
4080
|
-
async handleAssistantDelta(params) {
|
|
4081
|
-
const itemId = readString$5(params, "itemId") ?? readString$5(params, "id") ?? "assistant";
|
|
4082
|
-
const delta = readString$5(params, "delta") ?? "";
|
|
4083
|
-
if (!delta) return;
|
|
4084
|
-
if (itemId !== this.pendingRawTerminalAssistantEchoItemId) this.pendingRawTerminalAssistantEchoItemId = void 0;
|
|
4085
|
-
this.rememberAssistantPhase(readItem(params.item));
|
|
4086
|
-
const phase = readString$5(params, "phase");
|
|
4087
|
-
if (phase) this.assistantPhaseByItem.set(itemId, phase);
|
|
4088
|
-
const isCommentary = this.isCommentaryAssistantItem(itemId);
|
|
4089
|
-
if (!isCommentary && itemId !== this.latestTerminalAssistantCandidateItemId) this.markTerminalAssistantCandidateSupersededBy();
|
|
4090
|
-
if (!this.assistantStarted) {
|
|
4091
|
-
this.assistantStarted = true;
|
|
4092
|
-
await this.params.onAssistantMessageStart?.();
|
|
4093
|
-
}
|
|
4094
|
-
this.rememberAssistantItem(itemId);
|
|
4095
|
-
const text = `${this.assistantTextByItem.get(itemId) ?? ""}${delta}`;
|
|
4096
|
-
this.assistantTextByItem.set(itemId, text);
|
|
4097
|
-
if (isCommentary) this.emitCommentaryProgress({
|
|
4098
|
-
itemId,
|
|
4099
|
-
text
|
|
4100
|
-
});
|
|
4101
|
-
else {
|
|
4102
|
-
const knownFinalAnswer = this.shouldStreamAssistantPartial(itemId);
|
|
4103
|
-
const replace = this.streamedPartialAssistantItemId !== void 0 && this.streamedPartialAssistantItemId !== itemId;
|
|
4104
|
-
if (replace && (!knownFinalAnswer || this.streamedPartialAssistantItemReplaceable)) this.streamedPartialAssistantItemReplaceable = true;
|
|
4105
|
-
else if (this.streamedPartialAssistantItemId === void 0) this.streamedPartialAssistantItemReplaceable = !knownFinalAnswer;
|
|
4106
|
-
this.streamedPartialAssistantItemId = itemId;
|
|
4107
|
-
const replaceable = this.streamedPartialAssistantItemReplaceable;
|
|
4108
|
-
const replacement = replace && replaceable;
|
|
4109
|
-
const streamPayload = {
|
|
4110
|
-
text,
|
|
4111
|
-
delta: replacement ? "" : delta,
|
|
4112
|
-
...replacement ? { replace: true } : {}
|
|
4113
|
-
};
|
|
4114
|
-
this.emitAgentEvent({
|
|
4115
|
-
stream: "assistant",
|
|
4116
|
-
data: {
|
|
4117
|
-
...streamPayload,
|
|
4118
|
-
...replaceable ? { replaceable: true } : {}
|
|
4119
|
-
}
|
|
4120
|
-
});
|
|
4121
|
-
if (knownFinalAnswer && !replaceable) await this.params.onPartialReply?.(streamPayload);
|
|
4122
|
-
}
|
|
4123
|
-
}
|
|
4124
|
-
async handleReasoningDelta(method, params) {
|
|
4125
|
-
const itemId = readString$5(params, "itemId") ?? readString$5(params, "id") ?? "reasoning";
|
|
4126
|
-
const delta = readString$5(params, "delta") ?? "";
|
|
4127
|
-
if (!delta) return;
|
|
4128
|
-
this.reasoningStarted = true;
|
|
4129
|
-
if (!this.reasoningItemOrder.has(itemId)) this.reasoningItemOrder.set(itemId, this.reasoningItemOrder.size);
|
|
4130
|
-
const groupIndex = method === "item/reasoning/textDelta" ? readNonNegativeInteger(params, "contentIndex") ?? 0 : readNonNegativeInteger(params, "summaryIndex") ?? 0;
|
|
4131
|
-
const groupKey = `${method}\0${itemId}\0${groupIndex}`;
|
|
4132
|
-
const current = this.reasoningTextByGroup.get(groupKey);
|
|
4133
|
-
this.reasoningTextByGroup.set(groupKey, {
|
|
4134
|
-
itemId,
|
|
4135
|
-
method,
|
|
4136
|
-
index: groupIndex,
|
|
4137
|
-
text: `${current?.text ?? ""}${delta}`
|
|
4138
|
-
});
|
|
4139
|
-
await this.params.onReasoningStream?.({
|
|
4140
|
-
text: collectReasoningTextValues(this.reasoningTextByGroup, this.reasoningItemOrder).join("\n\n"),
|
|
4141
|
-
isReasoningSnapshot: true
|
|
4142
|
-
});
|
|
4143
|
-
}
|
|
4144
|
-
handlePlanDelta(params) {
|
|
4145
|
-
const itemId = readString$5(params, "itemId") ?? readString$5(params, "id") ?? "plan";
|
|
4146
|
-
const delta = readString$5(params, "delta") ?? "";
|
|
4147
|
-
if (!delta) return;
|
|
4148
|
-
const text = `${this.planTextByItem.get(itemId) ?? ""}${delta}`;
|
|
4149
|
-
this.planTextByItem.set(itemId, text);
|
|
4150
|
-
this.emitPlanUpdate({
|
|
4151
|
-
explanation: void 0,
|
|
4152
|
-
steps: splitPlanText(text)
|
|
4153
|
-
});
|
|
4154
|
-
}
|
|
4155
|
-
handleTurnPlanUpdated(params) {
|
|
4156
|
-
const plan = Array.isArray(params.plan) ? params.plan.flatMap((entry) => {
|
|
4157
|
-
if (!isJsonObject(entry)) return [];
|
|
4158
|
-
const step = readString$5(entry, "step");
|
|
4159
|
-
const status = readString$5(entry, "status");
|
|
4160
|
-
if (!step) return [];
|
|
4161
|
-
return status ? [`${step} (${status})`] : [step];
|
|
4162
|
-
}) : void 0;
|
|
4163
|
-
this.emitPlanUpdate({
|
|
4164
|
-
explanation: readNullableString$1(params, "explanation"),
|
|
4165
|
-
steps: plan
|
|
4166
|
-
});
|
|
4167
|
-
}
|
|
4168
|
-
async handleItemStarted(params) {
|
|
4169
|
-
const item = readItem(params.item);
|
|
4170
|
-
const itemId = item?.id ?? readString$5(params, "itemId") ?? readString$5(params, "id");
|
|
4171
|
-
if (item?.type === "agentMessage" && itemId && itemId !== this.pendingRawTerminalAssistantEchoItemId) this.pendingRawTerminalAssistantEchoItemId = void 0;
|
|
4172
|
-
this.rememberAssistantPhase(item);
|
|
4173
|
-
if (itemId) {
|
|
4174
|
-
this.activeItemIds.add(itemId);
|
|
4175
|
-
if (itemId !== this.latestTerminalAssistantCandidateItemId) {
|
|
4176
|
-
this.markTerminalAssistantCandidateSupersededBy(itemId, { preserveEarlierActiveItem: true });
|
|
4177
|
-
if (this.latestTerminalAssistantCandidateSuperseded) this.pendingRawTerminalAssistantEchoItemId = void 0;
|
|
4178
|
-
}
|
|
4179
|
-
}
|
|
4180
|
-
this.recordNativeToolOutcome(item);
|
|
4181
|
-
if (item?.type === "contextCompaction" && itemId) {
|
|
4182
|
-
this.activeCompactionItemIds.add(itemId);
|
|
4183
|
-
await runAgentHarnessBeforeCompactionHook({
|
|
4184
|
-
sessionFile: this.params.sessionFile,
|
|
4185
|
-
messages: await this.readMirroredSessionMessages(),
|
|
4186
|
-
ctx: {
|
|
4187
|
-
runId: this.params.runId,
|
|
4188
|
-
agentId: this.params.agentId,
|
|
4189
|
-
sessionKey: this.params.sessionKey,
|
|
4190
|
-
sessionId: this.params.sessionId,
|
|
4191
|
-
workspaceDir: this.params.workspaceDir,
|
|
4192
|
-
messageProvider: this.params.messageProvider ?? void 0,
|
|
4193
|
-
trigger: this.params.trigger,
|
|
4194
|
-
channelId: this.params.messageChannel ?? this.params.messageProvider ?? void 0
|
|
4195
|
-
}
|
|
4196
|
-
});
|
|
4197
|
-
this.emitAgentEvent({
|
|
4198
|
-
stream: "compaction",
|
|
4199
|
-
data: {
|
|
4200
|
-
phase: "start",
|
|
4201
|
-
backend: "codex-app-server",
|
|
4202
|
-
threadId: this.threadId,
|
|
4203
|
-
turnId: this.turnId,
|
|
4204
|
-
itemId
|
|
4205
|
-
}
|
|
4206
|
-
});
|
|
4207
|
-
}
|
|
4208
|
-
this.recordToolMeta(item);
|
|
4209
|
-
this.emitStandardItemEvent({
|
|
4210
|
-
phase: "start",
|
|
4211
|
-
item
|
|
4212
|
-
});
|
|
4213
|
-
await this.emitNormalizedToolItemEvent({
|
|
4214
|
-
phase: "start",
|
|
4215
|
-
item
|
|
4216
|
-
});
|
|
4217
|
-
this.recordNativeToolTranscriptCall(item);
|
|
4218
|
-
this.emitToolResultSummary(item);
|
|
4219
|
-
this.emitAgentEvent({
|
|
4220
|
-
stream: "codex_app_server.item",
|
|
4221
|
-
data: {
|
|
4222
|
-
phase: "started",
|
|
4223
|
-
itemId,
|
|
4224
|
-
type: item?.type
|
|
4225
|
-
}
|
|
4226
|
-
});
|
|
4227
|
-
}
|
|
4228
|
-
async handleItemCompleted(params) {
|
|
4229
|
-
const item = readItem(params.item);
|
|
4230
|
-
this.recordNativeToolOutcome(item);
|
|
4231
|
-
this.clearTerminalPresentationForNativeItem(item);
|
|
4232
|
-
const itemId = item?.id ?? readString$5(params, "itemId") ?? readString$5(params, "id");
|
|
4233
|
-
if (item?.type === "agentMessage" && itemId && itemId !== this.pendingRawTerminalAssistantEchoItemId) this.pendingRawTerminalAssistantEchoItemId = void 0;
|
|
4234
|
-
if (itemId) {
|
|
4235
|
-
this.activeItemIds.delete(itemId);
|
|
4236
|
-
this.completedItemIds.add(itemId);
|
|
4237
|
-
this.latestCompletedItemId = itemId;
|
|
4238
|
-
}
|
|
4239
|
-
this.rememberAssistantPhase(item);
|
|
4240
|
-
if (item?.type === "agentMessage" && !this.isCommentaryAssistantItem(item.id)) {
|
|
4241
|
-
this.latestCompletedTerminalAssistantItemId = item.id;
|
|
4242
|
-
this.markLatestTerminalAssistantCandidate(item.id);
|
|
4243
|
-
this.pendingRawTerminalAssistantEchoItemId = item.id;
|
|
4244
|
-
} else if (itemId) {
|
|
4245
|
-
this.markTerminalAssistantCandidateSupersededBy(itemId, { preserveEarlierActiveItem: true });
|
|
4246
|
-
if (this.latestTerminalAssistantCandidateSuperseded) this.pendingRawTerminalAssistantEchoItemId = void 0;
|
|
4247
|
-
}
|
|
4248
|
-
if (item?.type === "agentMessage" && typeof item.text === "string") {
|
|
4249
|
-
this.rememberAssistantItem(item.id);
|
|
4250
|
-
this.assistantTextByItem.set(item.id, item.text);
|
|
4251
|
-
if (item.text && this.isCommentaryAssistantItem(item.id)) {
|
|
4252
|
-
this.emitCommentaryProgress({
|
|
4253
|
-
itemId: item.id,
|
|
4254
|
-
text: item.text
|
|
4255
|
-
});
|
|
4256
|
-
this.pendingRawCommentaryEchoes += 1;
|
|
4257
|
-
}
|
|
4258
|
-
}
|
|
4259
|
-
this.recordNativeGeneratedMedia(item);
|
|
4260
|
-
if (item?.type === "plan" && typeof item.text === "string" && item.text) {
|
|
4261
|
-
this.planTextByItem.set(item.id, item.text);
|
|
4262
|
-
this.emitPlanUpdate({
|
|
4263
|
-
explanation: void 0,
|
|
4264
|
-
steps: splitPlanText(item.text)
|
|
4265
|
-
});
|
|
4266
|
-
}
|
|
4267
|
-
if (item?.type === "contextCompaction" && itemId) {
|
|
4268
|
-
this.activeCompactionItemIds.delete(itemId);
|
|
4269
|
-
this.completedCompactionCount += 1;
|
|
4270
|
-
await runAgentHarnessAfterCompactionHook({
|
|
4271
|
-
sessionFile: this.params.sessionFile,
|
|
4272
|
-
messages: await this.readMirroredSessionMessages(),
|
|
4273
|
-
compactedCount: -1,
|
|
4274
|
-
ctx: {
|
|
4275
|
-
runId: this.params.runId,
|
|
4276
|
-
agentId: this.params.agentId,
|
|
4277
|
-
sessionKey: this.params.sessionKey,
|
|
4278
|
-
sessionId: this.params.sessionId,
|
|
4279
|
-
workspaceDir: this.params.workspaceDir,
|
|
4280
|
-
messageProvider: this.params.messageProvider ?? void 0,
|
|
4281
|
-
trigger: this.params.trigger,
|
|
4282
|
-
channelId: this.params.messageChannel ?? this.params.messageProvider ?? void 0
|
|
4283
|
-
}
|
|
4284
|
-
});
|
|
4285
|
-
this.emitAgentEvent({
|
|
4286
|
-
stream: "compaction",
|
|
4287
|
-
data: {
|
|
4288
|
-
phase: "end",
|
|
4289
|
-
backend: "codex-app-server",
|
|
4290
|
-
completed: true,
|
|
4291
|
-
threadId: this.threadId,
|
|
4292
|
-
turnId: this.turnId,
|
|
4293
|
-
itemId
|
|
4294
|
-
}
|
|
4295
|
-
});
|
|
4296
|
-
}
|
|
4297
|
-
this.recordToolMeta(item);
|
|
4298
|
-
this.emitStandardItemEvent({
|
|
4299
|
-
phase: "end",
|
|
4300
|
-
item
|
|
4301
|
-
});
|
|
4302
|
-
await this.emitNormalizedToolItemEvent({
|
|
4303
|
-
phase: "result",
|
|
4304
|
-
item
|
|
4305
|
-
});
|
|
4306
|
-
this.recordNativeToolTranscriptCall(item);
|
|
4307
|
-
this.recordNativeToolTranscriptResult(item);
|
|
4308
|
-
this.emitToolResultSummary(item);
|
|
4309
|
-
this.emitToolResultOutput(item);
|
|
4310
|
-
this.emitAgentEvent({
|
|
4311
|
-
stream: "codex_app_server.item",
|
|
4312
|
-
data: {
|
|
4313
|
-
phase: "completed",
|
|
4314
|
-
itemId,
|
|
4315
|
-
type: item?.type
|
|
4316
|
-
}
|
|
4317
|
-
});
|
|
4318
|
-
}
|
|
4319
|
-
handleTokenUsage(params) {
|
|
4320
|
-
const tokenUsage = isJsonObject(params.tokenUsage) ? params.tokenUsage : void 0;
|
|
4321
|
-
const current = (tokenUsage ? readFirstJsonObject(tokenUsage, CURRENT_TOKEN_USAGE_KEYS) : void 0) ?? readFirstJsonObject(params, CURRENT_TOKEN_USAGE_KEYS);
|
|
4322
|
-
if (!current) return;
|
|
4323
|
-
const usage = normalizeCodexTokenUsage(current);
|
|
4324
|
-
if (usage) this.tokenUsage = usage;
|
|
4325
|
-
}
|
|
4326
|
-
handleGuardianReviewNotification(method, params) {
|
|
4327
|
-
this.guardianReviewCount += 1;
|
|
4328
|
-
const review = isJsonObject(params.review) ? params.review : void 0;
|
|
4329
|
-
const action = isJsonObject(params.action) ? params.action : void 0;
|
|
4330
|
-
this.emitAgentEvent({
|
|
4331
|
-
stream: "codex_app_server.guardian",
|
|
4332
|
-
data: {
|
|
4333
|
-
method,
|
|
4334
|
-
phase: method.endsWith("/started") ? "started" : "completed",
|
|
4335
|
-
reviewId: readString$5(params, "reviewId"),
|
|
4336
|
-
targetItemId: readNullableString$1(params, "targetItemId"),
|
|
4337
|
-
decisionSource: readString$5(params, "decisionSource"),
|
|
4338
|
-
status: review ? readString$5(review, "status") : void 0,
|
|
4339
|
-
riskLevel: review ? readString$5(review, "riskLevel") : void 0,
|
|
4340
|
-
userAuthorization: review ? readString$5(review, "userAuthorization") : void 0,
|
|
4341
|
-
rationale: review ? readNullableString$1(review, "rationale") : void 0,
|
|
4342
|
-
actionType: action ? readString$5(action, "type") : void 0
|
|
4343
|
-
}
|
|
4344
|
-
});
|
|
4345
|
-
}
|
|
4346
|
-
handleHookNotification(method, params) {
|
|
4347
|
-
const run = isJsonObject(params.run) ? params.run : void 0;
|
|
4348
|
-
if (!run) return;
|
|
4349
|
-
const durationMs = readNumber$1(run, "durationMs");
|
|
4350
|
-
const entries = readHookOutputEntries(run.entries);
|
|
4351
|
-
const hookTurnId = readNullableString$1(params, "turnId");
|
|
4352
|
-
this.emitAgentEvent({
|
|
4353
|
-
stream: "codex_app_server.hook",
|
|
4354
|
-
data: {
|
|
4355
|
-
phase: method === "hook/started" ? "started" : "completed",
|
|
4356
|
-
threadId: this.threadId,
|
|
4357
|
-
turnId: hookTurnId === void 0 ? this.turnId : hookTurnId,
|
|
4358
|
-
hookRunId: readString$5(run, "id"),
|
|
4359
|
-
eventName: readString$5(run, "eventName"),
|
|
4360
|
-
handlerType: readString$5(run, "handlerType"),
|
|
4361
|
-
executionMode: readString$5(run, "executionMode"),
|
|
4362
|
-
scope: readString$5(run, "scope"),
|
|
4363
|
-
source: readString$5(run, "source"),
|
|
4364
|
-
sourcePath: readString$5(run, "sourcePath"),
|
|
4365
|
-
status: readString$5(run, "status"),
|
|
4366
|
-
statusMessage: readNullableString$1(run, "statusMessage"),
|
|
4367
|
-
...durationMs !== void 0 ? { durationMs } : {},
|
|
4368
|
-
...entries.length > 0 ? { entries } : {}
|
|
4369
|
-
}
|
|
4370
|
-
});
|
|
4371
|
-
}
|
|
4372
|
-
async handleTurnCompleted(params) {
|
|
4373
|
-
const turn = readTurn(params.turn);
|
|
4374
|
-
if (!turn || turn.id !== this.turnId) return;
|
|
4375
|
-
this.completedTurn = turn;
|
|
4376
|
-
if (turn.status === "failed") {
|
|
4377
|
-
this.promptError = formatCodexUsageLimitErrorMessage({
|
|
4378
|
-
message: turn.error?.message,
|
|
4379
|
-
codexErrorInfo: turn.error?.codexErrorInfo,
|
|
4380
|
-
rateLimits: this.latestRateLimits ?? readRecentCodexRateLimits()
|
|
4381
|
-
}) ?? turn.error?.message ?? "codex app-server turn failed";
|
|
4382
|
-
this.promptErrorSource = "prompt";
|
|
4383
|
-
}
|
|
4384
|
-
const turnItems = turn.items ?? [];
|
|
4385
|
-
for (let index = turnItems.length - 1; index >= 0; index -= 1) {
|
|
4386
|
-
const item = turnItems[index];
|
|
4387
|
-
if (!item || !this.isCurrentTurnSnapshotItem(item)) continue;
|
|
4388
|
-
if (item?.type === "dynamicToolCall") break;
|
|
4389
|
-
if (shouldClearTerminalPresentationForNativeItem(item)) {
|
|
4390
|
-
this.clearTerminalPresentationForNativeItem(item);
|
|
4391
|
-
break;
|
|
4392
|
-
}
|
|
4393
|
-
}
|
|
4394
|
-
for (const item of turnItems) {
|
|
4395
|
-
this.rememberAssistantPhase(item);
|
|
4396
|
-
if (item.type === "agentMessage" && typeof item.text === "string") {
|
|
4397
|
-
this.rememberAssistantItem(item.id);
|
|
4398
|
-
this.assistantTextByItem.set(item.id, item.text);
|
|
4399
|
-
}
|
|
4400
|
-
this.recordNativeGeneratedMedia(item);
|
|
4401
|
-
if (item.type === "plan" && typeof item.text === "string" && item.text) {
|
|
4402
|
-
this.planTextByItem.set(item.id, item.text);
|
|
4403
|
-
this.emitPlanUpdate({
|
|
4404
|
-
explanation: void 0,
|
|
4405
|
-
steps: splitPlanText(item.text)
|
|
4406
|
-
});
|
|
4407
|
-
}
|
|
4408
|
-
this.recordToolMeta(item);
|
|
4409
|
-
await this.emitSnapshotOnlyNativeToolProgress(item);
|
|
4410
|
-
this.recordNativeToolTranscriptCall(item);
|
|
4411
|
-
this.recordNativeToolTranscriptResult(item);
|
|
4412
|
-
this.emitAfterToolCallObservation(item);
|
|
4413
|
-
this.emitToolResultSummary(item);
|
|
4414
|
-
this.emitToolResultOutput(item);
|
|
4415
|
-
}
|
|
4416
|
-
this.activeCompactionItemIds.clear();
|
|
4417
|
-
await this.maybeEndReasoning();
|
|
4418
|
-
}
|
|
4419
|
-
async emitSnapshotOnlyNativeToolProgress(item) {
|
|
4420
|
-
if (!shouldSynthesizeToolProgressForItem(item) || !this.isCurrentTurnSnapshotItem(item) || this.completedItemIds.has(item.id) || itemStatus(item) === "running") return;
|
|
4421
|
-
if (!this.activeItemIds.has(item.id)) {
|
|
4422
|
-
this.emitStandardItemEvent({
|
|
4423
|
-
phase: "start",
|
|
4424
|
-
item
|
|
4425
|
-
});
|
|
4426
|
-
await this.emitNormalizedToolItemEvent({
|
|
4427
|
-
phase: "start",
|
|
4428
|
-
item
|
|
4429
|
-
});
|
|
4430
|
-
}
|
|
4431
|
-
this.activeItemIds.delete(item.id);
|
|
4432
|
-
this.emitStandardItemEvent({
|
|
4433
|
-
phase: "end",
|
|
4434
|
-
item
|
|
4435
|
-
});
|
|
4436
|
-
await this.emitNormalizedToolItemEvent({
|
|
4437
|
-
phase: "result",
|
|
4438
|
-
item
|
|
4439
|
-
});
|
|
4440
|
-
this.completedItemIds.add(item.id);
|
|
4441
|
-
}
|
|
4442
|
-
isCurrentTurnSnapshotItem(item) {
|
|
4443
|
-
const itemTurnId = readItemString(item, "turnId") ?? readItemString(item, "turn_id");
|
|
4444
|
-
return itemTurnId === void 0 || itemTurnId === this.turnId;
|
|
4445
|
-
}
|
|
4446
|
-
handleOutputDelta(params, toolName) {
|
|
4447
|
-
const itemId = readString$5(params, "itemId");
|
|
4448
|
-
const delta = readString$5(params, "delta");
|
|
4449
|
-
if (!itemId || !delta) return;
|
|
4450
|
-
appendToolOutputDeltaText(this.toolResultOutputTextByItem, itemId, delta);
|
|
4451
|
-
if (!this.shouldEmitToolOutput()) return;
|
|
4452
|
-
if (this.transcriptToolProgressSuppressedIds.has(itemId) || !shouldEmitTranscriptToolProgress(toolName, this.toolTranscriptArgumentsById.get(itemId))) return;
|
|
4453
|
-
const state = this.toolResultOutputDeltaState.get(itemId) ?? {
|
|
4454
|
-
chars: 0,
|
|
4455
|
-
messages: 0,
|
|
4456
|
-
truncated: false
|
|
4457
|
-
};
|
|
4458
|
-
if (state.truncated) return;
|
|
4459
|
-
const remainingChars = Math.max(0, TOOL_PROGRESS_OUTPUT_MAX_CHARS - state.chars);
|
|
4460
|
-
const remainingMessages = Math.max(0, MAX_TOOL_OUTPUT_DELTA_MESSAGES_PER_ITEM - state.messages);
|
|
4461
|
-
if (remainingChars === 0 || remainingMessages === 0) {
|
|
4462
|
-
state.truncated = true;
|
|
4463
|
-
this.toolResultOutputDeltaState.set(itemId, state);
|
|
4464
|
-
this.emitToolResultMessage({
|
|
4465
|
-
itemId,
|
|
4466
|
-
text: formatToolOutput(toolName, void 0, "(output truncated)")
|
|
4467
|
-
});
|
|
4468
|
-
return;
|
|
4469
|
-
}
|
|
4470
|
-
const chunk = delta.length > remainingChars ? delta.slice(0, remainingChars) : delta;
|
|
4471
|
-
state.chars += chunk.length;
|
|
4472
|
-
state.messages += 1;
|
|
4473
|
-
const reachedLimit = delta.length > remainingChars || state.chars >= TOOL_PROGRESS_OUTPUT_MAX_CHARS || state.messages >= MAX_TOOL_OUTPUT_DELTA_MESSAGES_PER_ITEM;
|
|
4474
|
-
if (reachedLimit) state.truncated = true;
|
|
4475
|
-
this.toolResultOutputDeltaState.set(itemId, state);
|
|
4476
|
-
this.toolResultOutputStreamedItemIds.add(itemId);
|
|
4477
|
-
this.emitToolResultMessage({
|
|
4478
|
-
itemId,
|
|
4479
|
-
text: formatToolOutput(toolName, void 0, reachedLimit ? `${chunk}\n...(truncated)...` : chunk)
|
|
4480
|
-
});
|
|
4481
|
-
}
|
|
4482
|
-
async handleRawResponseItemCompleted(params) {
|
|
4483
|
-
const item = isJsonObject(params.item) ? params.item : void 0;
|
|
4484
|
-
if (!item) return;
|
|
4485
|
-
const role = readString$5(item, "role");
|
|
4486
|
-
const phase = readString$5(item, "phase");
|
|
4487
|
-
const rawItemId = readString$5(item, "id");
|
|
4488
|
-
const candidateWasSupersededBeforeRaw = this.latestTerminalAssistantCandidateSuperseded;
|
|
4489
|
-
const pendingTerminalAssistantEchoItemId = this.pendingRawTerminalAssistantEchoItemId;
|
|
4490
|
-
const isPendingTerminalAssistantEcho = role === "assistant" && phase !== "commentary" && pendingTerminalAssistantEchoItemId !== void 0 && (rawItemId === void 0 || rawItemId === pendingTerminalAssistantEchoItemId);
|
|
4491
|
-
if (pendingTerminalAssistantEchoItemId !== void 0 && !isPendingTerminalAssistantEcho) this.pendingRawTerminalAssistantEchoItemId = void 0;
|
|
4492
|
-
if (!isPendingTerminalAssistantEcho) {
|
|
4493
|
-
this.latestCompletedItemId = void 0;
|
|
4494
|
-
this.markTerminalAssistantCandidateSupersededBy(rawItemId);
|
|
4495
|
-
}
|
|
4496
|
-
await this.recordRawGeneratedImageMedia(item);
|
|
4497
|
-
if (role !== "assistant") return;
|
|
4498
|
-
if (phase === "commentary" && this.pendingRawCommentaryEchoes > 0) {
|
|
4499
|
-
this.pendingRawCommentaryEchoes -= 1;
|
|
4500
|
-
return;
|
|
4501
|
-
}
|
|
4502
|
-
const text = extractRawAssistantText(item);
|
|
4503
|
-
if (isPendingTerminalAssistantEcho) {
|
|
4504
|
-
const typedItemId = pendingTerminalAssistantEchoItemId;
|
|
4505
|
-
this.pendingRawTerminalAssistantEchoItemId = void 0;
|
|
4506
|
-
if (this.assistantTextByItem.get(typedItemId)?.trim() || !text) return;
|
|
4507
|
-
this.rememberAssistantItem(typedItemId);
|
|
4508
|
-
this.assistantTextByItem.set(typedItemId, text);
|
|
4509
|
-
return;
|
|
4510
|
-
}
|
|
4511
|
-
if (!text) return;
|
|
4512
|
-
const itemId = rawItemId ?? `raw-assistant-${this.assistantItemOrder.length + 1}`;
|
|
4513
|
-
const isIdlessTerminalAssistantAfterCompletedWork = candidateWasSupersededBeforeRaw && rawItemId === void 0 && pendingTerminalAssistantEchoItemId === void 0 && this.activeItemIds.size === 0;
|
|
4514
|
-
if (phase !== "commentary" && candidateWasSupersededBeforeRaw && itemId !== this.streamedPartialAssistantItemId && !isIdlessTerminalAssistantAfterCompletedWork) return;
|
|
4515
|
-
if (phase) this.assistantPhaseByItem.set(itemId, phase);
|
|
4516
|
-
this.rememberAssistantItem(itemId);
|
|
4517
|
-
this.assistantTextByItem.set(itemId, text);
|
|
4518
|
-
if (phase === "commentary") this.emitCommentaryProgress({
|
|
4519
|
-
itemId,
|
|
4520
|
-
text
|
|
4521
|
-
});
|
|
4522
|
-
else this.markLatestTerminalAssistantCandidate(itemId, { canReleaseAfterToolHandoff: isIdlessTerminalAssistantAfterCompletedWork });
|
|
4523
|
-
}
|
|
4524
|
-
markLatestTerminalAssistantCandidate(itemId, options) {
|
|
4525
|
-
this.latestTerminalAssistantCandidateItemId = itemId;
|
|
4526
|
-
this.latestTerminalAssistantCandidateSuperseded = false;
|
|
4527
|
-
this.latestTerminalAssistantCandidateCanReleaseAfterToolHandoff = options?.canReleaseAfterToolHandoff === true;
|
|
4528
|
-
this.terminalAssistantCandidateEarlierActiveItemIds = new Set(this.activeItemIds);
|
|
4529
|
-
}
|
|
4530
|
-
markTerminalAssistantCandidateSupersededBy(itemId, options) {
|
|
4531
|
-
if (!this.latestTerminalAssistantCandidateItemId) return;
|
|
4532
|
-
if (itemId && this.terminalAssistantCandidateEarlierActiveItemIds.has(itemId)) {
|
|
4533
|
-
if (!options?.preserveEarlierActiveItem) this.terminalAssistantCandidateEarlierActiveItemIds.delete(itemId);
|
|
4534
|
-
return;
|
|
4535
|
-
}
|
|
4536
|
-
this.latestTerminalAssistantCandidateSuperseded = true;
|
|
4537
|
-
this.latestTerminalAssistantCandidateCanReleaseAfterToolHandoff = false;
|
|
4538
|
-
this.terminalAssistantCandidateEarlierActiveItemIds.clear();
|
|
4539
|
-
}
|
|
4540
|
-
recordNativeGeneratedMedia(item) {
|
|
4541
|
-
if (item?.type !== "imageGeneration") return;
|
|
4542
|
-
const savedPath = readItemString(item, "savedPath")?.trim();
|
|
4543
|
-
if (savedPath) this.recordNativeGeneratedMediaUrl({
|
|
4544
|
-
itemId: item.id,
|
|
4545
|
-
mediaUrl: savedPath
|
|
4546
|
-
});
|
|
4547
|
-
}
|
|
4548
|
-
async recordRawGeneratedImageMedia(item) {
|
|
4549
|
-
if (readString$5(item, "type") !== "image_generation_call") return;
|
|
4550
|
-
const result = readString$5(item, "result");
|
|
4551
|
-
if (!result) return;
|
|
4552
|
-
const itemId = readString$5(item, "id") ?? `raw-image-${this.nativeGeneratedMediaItemIds.size}`;
|
|
4553
|
-
this.nativeGeneratedMediaItemIds.add(itemId);
|
|
4554
|
-
const maxBytes = resolveGeneratedImageMaxBytes(this.params.config);
|
|
4555
|
-
const estimatedDecodedBytes = estimateBase64DecodedBytes(result);
|
|
4556
|
-
if (estimatedDecodedBytes !== void 0 && estimatedDecodedBytes > maxBytes) {
|
|
4557
|
-
embeddedAgentLog.warn("codex app-server raw image generation result exceeds media limit", {
|
|
4558
|
-
itemId,
|
|
4559
|
-
estimatedDecodedBytes,
|
|
4560
|
-
maxBytes
|
|
4561
|
-
});
|
|
4562
|
-
return;
|
|
4563
|
-
}
|
|
4564
|
-
const asset = generatedImageAssetFromBase64({
|
|
4565
|
-
base64: result,
|
|
4566
|
-
index: this.nativeGeneratedMediaItemIds.size,
|
|
4567
|
-
revisedPrompt: readString$5(item, "revised_prompt") ?? readString$5(item, "revisedPrompt"),
|
|
4568
|
-
fileNamePrefix: "codex-image-generation",
|
|
4569
|
-
sniffMimeType: true
|
|
4570
|
-
});
|
|
4571
|
-
if (!asset) return;
|
|
4572
|
-
try {
|
|
4573
|
-
const saved = await saveMediaBuffer(asset.buffer, asset.mimeType, GENERATED_IMAGE_MEDIA_SUBDIR, maxBytes, asset.fileName);
|
|
4574
|
-
this.recordNativeGeneratedMediaUrl({
|
|
4575
|
-
itemId,
|
|
4576
|
-
mediaUrl: saved.path,
|
|
4577
|
-
replaceExisting: true
|
|
4578
|
-
});
|
|
4579
|
-
} catch (error) {
|
|
4580
|
-
embeddedAgentLog.warn("codex app-server raw image generation result save failed", {
|
|
4581
|
-
itemId,
|
|
4582
|
-
error
|
|
4583
|
-
});
|
|
4584
|
-
}
|
|
4585
|
-
}
|
|
4586
|
-
recordNativeGeneratedMediaUrl(params) {
|
|
4587
|
-
if (this.nativeGeneratedMediaUrlsByItemId.has(params.itemId) && params.replaceExisting !== true) {
|
|
4588
|
-
this.nativeGeneratedMediaItemIds.add(params.itemId);
|
|
4589
|
-
return;
|
|
4590
|
-
}
|
|
4591
|
-
this.nativeGeneratedMediaUrlsByItemId.set(params.itemId, params.mediaUrl);
|
|
4592
|
-
this.nativeGeneratedMediaItemIds.add(params.itemId);
|
|
4593
|
-
}
|
|
4594
|
-
buildToolMediaUrls(toolTelemetry) {
|
|
4595
|
-
const mediaUrls = new Set(toolTelemetry.toolMediaUrls?.map((url) => url.trim()).filter(Boolean) ?? []);
|
|
4596
|
-
if ((toolTelemetry.messagingToolSentMediaUrls?.length ?? 0) === 0) for (const mediaUrl of this.nativeGeneratedMediaUrlsByItemId.values()) mediaUrls.add(mediaUrl);
|
|
4597
|
-
return mediaUrls.size > 0 ? [...mediaUrls] : toolTelemetry.toolMediaUrls;
|
|
4598
|
-
}
|
|
4599
|
-
async maybeEndReasoning() {
|
|
4600
|
-
if (!this.reasoningStarted || this.reasoningEnded) return;
|
|
4601
|
-
this.reasoningEnded = true;
|
|
4602
|
-
await this.params.onReasoningEnd?.();
|
|
4603
|
-
}
|
|
4604
|
-
emitPlanUpdate(params) {
|
|
4605
|
-
if (!params.explanation && (!params.steps || params.steps.length === 0)) return;
|
|
4606
|
-
this.emitAgentEvent({
|
|
4607
|
-
stream: "plan",
|
|
4608
|
-
data: {
|
|
4609
|
-
phase: "update",
|
|
4610
|
-
title: "Plan updated",
|
|
4611
|
-
source: "codex-app-server",
|
|
4612
|
-
...params.explanation ? { explanation: params.explanation } : {},
|
|
4613
|
-
...params.steps && params.steps.length > 0 ? { steps: params.steps } : {}
|
|
4614
|
-
}
|
|
4615
|
-
});
|
|
4616
|
-
}
|
|
4617
|
-
rememberAssistantPhase(item) {
|
|
4618
|
-
if (item?.type !== "agentMessage") return;
|
|
4619
|
-
const phase = readItemString(item, "phase");
|
|
4620
|
-
if (phase) this.assistantPhaseByItem.set(item.id, phase);
|
|
4621
|
-
}
|
|
4622
|
-
isCommentaryAssistantItem(itemId) {
|
|
4623
|
-
return this.assistantPhaseByItem.get(itemId) === "commentary";
|
|
4624
|
-
}
|
|
4625
|
-
shouldStreamAssistantPartial(itemId) {
|
|
4626
|
-
return this.assistantPhaseByItem.get(itemId) === "final_answer";
|
|
4627
|
-
}
|
|
4628
|
-
emitCommentaryProgress(params) {
|
|
4629
|
-
const progressText = params.text.replace(/\s+/g, " ").trim();
|
|
4630
|
-
if (!progressText || this.lastCommentaryProgressTextByItem.get(params.itemId) === progressText) return;
|
|
4631
|
-
this.lastCommentaryProgressTextByItem.set(params.itemId, progressText);
|
|
4632
|
-
this.emitAgentEvent({
|
|
4633
|
-
stream: "item",
|
|
4634
|
-
data: {
|
|
4635
|
-
itemId: params.itemId,
|
|
4636
|
-
kind: "preamble",
|
|
4637
|
-
title: "Preamble",
|
|
4638
|
-
phase: "update",
|
|
4639
|
-
progressText,
|
|
4640
|
-
source: "codex-app-server"
|
|
4641
|
-
}
|
|
4642
|
-
});
|
|
4643
|
-
}
|
|
4644
|
-
emitStandardItemEvent(params) {
|
|
4645
|
-
const { item } = params;
|
|
4646
|
-
if (!item) return;
|
|
4647
|
-
const kind = itemKind(item);
|
|
4648
|
-
if (!kind) return;
|
|
4649
|
-
const meta = itemMeta(item, this.toolProgressDetailMode());
|
|
4650
|
-
const suppressChannelProgress = shouldSuppressChannelProgressForItem(item);
|
|
4651
|
-
this.emitAgentEvent({
|
|
4652
|
-
stream: "item",
|
|
4653
|
-
data: {
|
|
4654
|
-
itemId: item.id,
|
|
4655
|
-
phase: params.phase,
|
|
4656
|
-
kind,
|
|
4657
|
-
title: itemTitle(item),
|
|
4658
|
-
status: params.phase === "start" ? "running" : itemStatus(item),
|
|
4659
|
-
...itemName(item) ? { name: itemName(item) } : {},
|
|
4660
|
-
...meta ? { meta } : {},
|
|
4661
|
-
...suppressChannelProgress ? { suppressChannelProgress: true } : {}
|
|
4662
|
-
}
|
|
4663
|
-
});
|
|
4664
|
-
}
|
|
4665
|
-
async emitNormalizedToolItemEvent(params) {
|
|
4666
|
-
const { item } = params;
|
|
4667
|
-
if (!item || !shouldSynthesizeToolProgressForItem(item)) return;
|
|
4668
|
-
const name = itemName(item);
|
|
4669
|
-
if (!name) return;
|
|
4670
|
-
const status = params.phase === "result" ? itemStatus(item) : "running";
|
|
4671
|
-
const args = itemToolArgs(item);
|
|
4672
|
-
const meta = itemMeta(item, this.toolProgressDetailMode());
|
|
4673
|
-
this.recordToolTrajectoryEvent({
|
|
4674
|
-
phase: params.phase,
|
|
4675
|
-
item,
|
|
4676
|
-
name,
|
|
4677
|
-
args,
|
|
4678
|
-
status
|
|
4679
|
-
});
|
|
4680
|
-
this.emitDiagnosticToolExecutionEvent({
|
|
4681
|
-
phase: params.phase,
|
|
4682
|
-
item,
|
|
4683
|
-
name,
|
|
4684
|
-
status
|
|
4685
|
-
});
|
|
4686
|
-
if (params.phase === "result") this.recordNativeToolError({
|
|
4687
|
-
item,
|
|
4688
|
-
name,
|
|
4689
|
-
meta,
|
|
4690
|
-
status
|
|
4691
|
-
});
|
|
4692
|
-
if (!shouldEmitTranscriptToolProgress(name, args)) {
|
|
4693
|
-
if (params.phase === "result") {
|
|
4694
|
-
this.emitAfterToolCallObservation(item);
|
|
4695
|
-
await this.options.onNativeToolResultRecorded?.();
|
|
4696
|
-
}
|
|
4697
|
-
return;
|
|
4698
|
-
}
|
|
4699
|
-
this.emitAgentEvent({
|
|
4700
|
-
stream: "tool",
|
|
4701
|
-
data: {
|
|
4702
|
-
phase: params.phase,
|
|
4703
|
-
name,
|
|
4704
|
-
itemId: item.id,
|
|
4705
|
-
toolCallId: item.id,
|
|
4706
|
-
...meta ? { meta } : {},
|
|
4707
|
-
...params.phase === "start" && args ? { args } : {},
|
|
4708
|
-
...params.phase === "result" ? {
|
|
4709
|
-
status,
|
|
4710
|
-
isError: isNonSuccessItemStatus(status),
|
|
4711
|
-
...itemToolResult(item)
|
|
4712
|
-
} : {}
|
|
4713
|
-
}
|
|
4714
|
-
});
|
|
4715
|
-
if (params.phase === "result") {
|
|
4716
|
-
this.emitAfterToolCallObservation(item);
|
|
4717
|
-
await this.options.onNativeToolResultRecorded?.();
|
|
4718
|
-
}
|
|
4719
|
-
}
|
|
4720
|
-
clearTerminalPresentationForNativeItem(item) {
|
|
4721
|
-
if (!item || this.terminalPresentationClearedItemIds.has(item.id) || !shouldClearTerminalPresentationForNativeItem(item)) return;
|
|
4722
|
-
const toolCallOrdinal = this.nativeToolOutcomeOrdinals.get(item.id);
|
|
4723
|
-
this.terminalPresentationClearedItemIds.add(item.id);
|
|
4724
|
-
this.params.onToolOutcome?.({
|
|
4725
|
-
toolName: itemName(item) ?? item.type,
|
|
4726
|
-
argsHash: "",
|
|
4727
|
-
resultHash: "",
|
|
4728
|
-
...toolCallOrdinal !== void 0 ? { toolCallOrdinal } : {},
|
|
4729
|
-
terminalPresentation: void 0,
|
|
4730
|
-
presentationOnly: true
|
|
4731
|
-
});
|
|
4732
|
-
}
|
|
4733
|
-
recordNativeToolError(params) {
|
|
4734
|
-
if (!isNonSuccessItemStatus(params.status)) {
|
|
4735
|
-
if (!this.lastNativeToolError) return;
|
|
4736
|
-
if (!this.lastNativeToolError.mutatingAction) {
|
|
4737
|
-
this.lastNativeToolError = void 0;
|
|
4738
|
-
return;
|
|
4739
|
-
}
|
|
4740
|
-
const actionFingerprint = nativeToolActionFingerprint(params.item);
|
|
4741
|
-
if (this.lastNativeToolError.actionFingerprint && actionFingerprint && this.lastNativeToolError.actionFingerprint === actionFingerprint) this.lastNativeToolError = void 0;
|
|
4742
|
-
return;
|
|
4743
|
-
}
|
|
4744
|
-
const error = itemToolError(params.item, params.status, this.toolResultOutputTextByItem);
|
|
4745
|
-
const actionFingerprint = nativeToolActionFingerprint(params.item);
|
|
4746
|
-
this.lastNativeToolError = {
|
|
4747
|
-
toolName: params.name,
|
|
4748
|
-
...params.meta ? { meta: params.meta } : {},
|
|
4749
|
-
...error ? { error } : {},
|
|
4750
|
-
...isMutatingNativeToolItem(params.item) ? { mutatingAction: true } : {},
|
|
4751
|
-
...actionFingerprint ? { actionFingerprint } : {}
|
|
4752
|
-
};
|
|
4753
|
-
}
|
|
4754
|
-
recordToolTrajectoryEvent(params) {
|
|
4755
|
-
if (params.phase === "start") {
|
|
4756
|
-
this.toolTrajectoryCallIds.add(params.item.id);
|
|
4757
|
-
this.toolTrajectoryNamesById.set(params.item.id, params.name);
|
|
4758
|
-
this.toolTrajectoryItemsById.set(params.item.id, params.item);
|
|
4759
|
-
this.options.trajectoryRecorder?.recordEvent("tool.call", {
|
|
4760
|
-
threadId: this.threadId,
|
|
4761
|
-
turnId: this.turnId,
|
|
4762
|
-
itemId: params.item.id,
|
|
4763
|
-
toolCallId: params.item.id,
|
|
4764
|
-
name: params.name,
|
|
4765
|
-
arguments: params.args
|
|
4766
|
-
});
|
|
4767
|
-
return;
|
|
4768
|
-
}
|
|
4769
|
-
this.toolTrajectoryResultIds.add(params.item.id);
|
|
4770
|
-
const toolResult = itemToolResult(params.item).result;
|
|
4771
|
-
const output = itemOutputText(params.item, this.toolResultOutputTextByItem);
|
|
4772
|
-
this.options.trajectoryRecorder?.recordEvent("tool.result", {
|
|
4773
|
-
threadId: this.threadId,
|
|
4774
|
-
turnId: this.turnId,
|
|
4775
|
-
itemId: params.item.id,
|
|
4776
|
-
toolCallId: params.item.id,
|
|
4777
|
-
name: params.name,
|
|
4778
|
-
status: params.status,
|
|
4779
|
-
isError: isNonSuccessItemStatus(params.status),
|
|
4780
|
-
...toolResult ? { result: toolResult } : {},
|
|
4781
|
-
...output ? { output } : {}
|
|
4782
|
-
});
|
|
4783
|
-
}
|
|
4784
|
-
emitDiagnosticToolExecutionEvent(params) {
|
|
4785
|
-
const base = {
|
|
4786
|
-
runId: this.params.runId,
|
|
4787
|
-
sessionId: this.params.sessionId,
|
|
4788
|
-
sessionKey: this.params.sessionKey,
|
|
4789
|
-
toolName: params.name,
|
|
4790
|
-
toolCallId: params.item.id
|
|
4791
|
-
};
|
|
4792
|
-
if (params.phase === "start") {
|
|
4793
|
-
this.diagnosticToolStartedAtByItem.set(params.item.id, Date.now());
|
|
4794
|
-
emitTrustedDiagnosticEvent({
|
|
4795
|
-
type: "tool.execution.started",
|
|
4796
|
-
...base
|
|
4797
|
-
});
|
|
4798
|
-
return;
|
|
4799
|
-
}
|
|
4800
|
-
const startedAt = this.diagnosticToolStartedAtByItem.get(params.item.id);
|
|
4801
|
-
this.diagnosticToolStartedAtByItem.delete(params.item.id);
|
|
4802
|
-
const durationMs = (typeof params.item.durationMs === "number" ? params.item.durationMs : void 0) ?? (startedAt === void 0 ? 0 : Math.max(0, Date.now() - startedAt));
|
|
4803
|
-
const terminalEvent = params.status === "blocked" ? {
|
|
4804
|
-
type: "tool.execution.blocked",
|
|
4805
|
-
reason: "codex_native_tool_blocked",
|
|
4806
|
-
deniedReason: "codex_native_tool_blocked"
|
|
4807
|
-
} : params.status === "failed" ? {
|
|
4808
|
-
type: "tool.execution.error",
|
|
4809
|
-
durationMs,
|
|
4810
|
-
errorCategory: "codex_native_tool_error"
|
|
4811
|
-
} : {
|
|
4812
|
-
type: "tool.execution.completed",
|
|
4813
|
-
durationMs
|
|
4814
|
-
};
|
|
4815
|
-
emitTrustedDiagnosticEvent({
|
|
4816
|
-
...base,
|
|
4817
|
-
...terminalEvent
|
|
4818
|
-
});
|
|
4819
|
-
}
|
|
4820
|
-
emitAfterToolCallObservation(item) {
|
|
4821
|
-
if (!this.shouldEmitAfterToolCallObservation(item)) return;
|
|
4822
|
-
const name = itemName(item);
|
|
4823
|
-
if (!name) return;
|
|
4824
|
-
const status = itemStatus(item);
|
|
4825
|
-
if (status === "running") return;
|
|
4826
|
-
this.afterToolCallObservedItemIds.add(item.id);
|
|
4827
|
-
const result = itemToolResult(item).result;
|
|
4828
|
-
const error = itemToolError(item, status, this.toolResultOutputTextByItem);
|
|
4829
|
-
const startedAt = resolveStartedAtFromDurationMs(item.durationMs);
|
|
4830
|
-
const hookParams = {
|
|
4831
|
-
toolName: name,
|
|
4832
|
-
toolCallId: item.id,
|
|
4833
|
-
runId: this.params.runId,
|
|
4834
|
-
agentId: this.params.agentId,
|
|
4835
|
-
sessionId: this.params.sessionId,
|
|
4836
|
-
sessionKey: this.params.sessionKey,
|
|
4837
|
-
startArgs: itemToolArgs(item) ?? {},
|
|
4838
|
-
...result !== void 0 ? { result } : {},
|
|
4839
|
-
...error ? { error } : {},
|
|
4840
|
-
...startedAt !== void 0 ? { startedAt } : {}
|
|
4841
|
-
};
|
|
4842
|
-
setImmediate(() => {
|
|
4843
|
-
runAgentHarnessAfterToolCallHook(hookParams);
|
|
4844
|
-
});
|
|
4845
|
-
}
|
|
4846
|
-
shouldEmitAfterToolCallObservation(item) {
|
|
4847
|
-
if (!shouldSynthesizeToolProgressForItem(item) || this.afterToolCallObservedItemIds.has(item.id)) return false;
|
|
4848
|
-
if (this.options.nativePostToolUseRelayEnabled && isNativePostToolUseRelayItem(item)) return false;
|
|
4849
|
-
return true;
|
|
4850
|
-
}
|
|
4851
|
-
emitToolResultSummary(item) {
|
|
4852
|
-
if (!item || !this.params.onToolResult || !this.shouldEmitToolResult()) return;
|
|
4853
|
-
const itemId = item.id;
|
|
4854
|
-
if (this.toolResultSummaryItemIds.has(itemId)) return;
|
|
4855
|
-
const toolName = itemName(item);
|
|
4856
|
-
if (!toolName) return;
|
|
4857
|
-
if (!shouldEmitTranscriptToolProgress(toolName, itemToolArgs(item))) return;
|
|
4858
|
-
this.toolResultSummaryItemIds.add(itemId);
|
|
4859
|
-
const meta = itemMeta(item, this.toolProgressDetailMode());
|
|
4860
|
-
this.emitToolResultMessage({
|
|
4861
|
-
itemId,
|
|
4862
|
-
text: formatToolSummary(toolName, meta)
|
|
4863
|
-
});
|
|
4864
|
-
}
|
|
4865
|
-
emitToolResultOutput(item) {
|
|
4866
|
-
if (!item || !this.params.onToolResult || !this.shouldEmitToolOutput()) return;
|
|
4867
|
-
const itemId = item.id;
|
|
4868
|
-
if (this.toolResultOutputItemIds.has(itemId)) return;
|
|
4869
|
-
if (this.toolResultOutputStreamedItemIds.has(itemId)) return;
|
|
4870
|
-
const toolName = itemName(item);
|
|
4871
|
-
const output = itemOutputText(item, this.toolResultOutputTextByItem);
|
|
4872
|
-
if (!toolName || !output) return;
|
|
4873
|
-
if (!shouldEmitTranscriptToolProgress(toolName, itemToolArgs(item))) return;
|
|
4874
|
-
this.emitToolResultMessage({
|
|
4875
|
-
itemId,
|
|
4876
|
-
text: formatToolOutput(toolName, itemMeta(item, this.toolProgressDetailMode()), output),
|
|
4877
|
-
finalOutput: true,
|
|
4878
|
-
isError: isNonSuccessItemStatus(itemStatus(item))
|
|
4879
|
-
});
|
|
4880
|
-
}
|
|
4881
|
-
emitToolResultMessage(params) {
|
|
4882
|
-
const text = params.text.trim();
|
|
4883
|
-
if (!text) return;
|
|
4884
|
-
this.toolProgressTexts.add(text);
|
|
4885
|
-
if (params.finalOutput) this.toolResultOutputItemIds.add(params.itemId);
|
|
4886
|
-
try {
|
|
4887
|
-
Promise.resolve(this.params.onToolResult?.({
|
|
4888
|
-
text,
|
|
4889
|
-
...params.isError === true ? { isError: true } : {}
|
|
4890
|
-
})).catch(() => {});
|
|
4891
|
-
} catch {}
|
|
4892
|
-
}
|
|
4893
|
-
shouldEmitToolResult() {
|
|
4894
|
-
return typeof this.params.shouldEmitToolResult === "function" ? this.params.shouldEmitToolResult() : this.params.verboseLevel === "on" || this.params.verboseLevel === "full";
|
|
4895
|
-
}
|
|
4896
|
-
shouldEmitToolOutput() {
|
|
4897
|
-
return typeof this.params.shouldEmitToolOutput === "function" ? this.params.shouldEmitToolOutput() : this.params.verboseLevel === "full";
|
|
4898
|
-
}
|
|
4899
|
-
toolProgressDetailMode() {
|
|
4900
|
-
return resolveCodexToolProgressDetailMode(this.params.toolProgressDetail);
|
|
4901
|
-
}
|
|
4902
|
-
recordToolMeta(item) {
|
|
4903
|
-
if (!item) return;
|
|
4904
|
-
if (isSideEffectingNativeToolItem(item)) this.sideEffectingToolItemIds.add(item.id);
|
|
4905
|
-
else this.sideEffectingToolItemIds.delete(item.id);
|
|
4906
|
-
const toolName = itemName(item);
|
|
4907
|
-
if (!toolName) return;
|
|
4908
|
-
const meta = itemMeta(item, this.toolProgressDetailMode());
|
|
4909
|
-
const existing = this.toolMetas.get(item.id);
|
|
4910
|
-
this.toolMetas.set(item.id, {
|
|
4911
|
-
toolName,
|
|
4912
|
-
...meta ? { meta } : {},
|
|
4913
|
-
...existing?.asyncStarted ? { asyncStarted: true } : {}
|
|
4914
|
-
});
|
|
4915
|
-
}
|
|
4916
|
-
recordNativeToolTranscriptCall(item) {
|
|
4917
|
-
if (!item || !shouldRecordNativeToolTranscript(item)) return;
|
|
4918
|
-
const name = itemName(item);
|
|
4919
|
-
if (!name) return;
|
|
4920
|
-
this.recordToolTranscriptCall({
|
|
4921
|
-
id: item.id,
|
|
4922
|
-
name,
|
|
4923
|
-
arguments: itemToolArgs(item)
|
|
4924
|
-
});
|
|
4925
|
-
}
|
|
4926
|
-
recordNativeToolTranscriptResult(item) {
|
|
4927
|
-
if (!item || !shouldRecordNativeToolTranscript(item)) return;
|
|
4928
|
-
const name = itemName(item);
|
|
4929
|
-
if (!name) return;
|
|
4930
|
-
this.recordToolTranscriptResult({
|
|
4931
|
-
id: item.id,
|
|
4932
|
-
name,
|
|
4933
|
-
text: itemTranscriptResultText(item, this.toolResultOutputTextByItem),
|
|
4934
|
-
isError: isNonSuccessItemStatus(itemStatus(item))
|
|
4935
|
-
});
|
|
4936
|
-
}
|
|
4937
|
-
recordToolTranscriptCall(params) {
|
|
4938
|
-
if (!params.id || !params.name || this.toolTranscriptCallIds.has(params.id)) return;
|
|
4939
|
-
this.toolTranscriptCallIds.add(params.id);
|
|
4940
|
-
this.toolTranscriptNamesById.set(params.id, params.name);
|
|
4941
|
-
this.toolTranscriptArgumentsById.set(params.id, params.arguments);
|
|
4942
|
-
if (!shouldEmitTranscriptToolProgress(params.name, params.arguments)) this.transcriptToolProgressSuppressedIds.add(params.id);
|
|
4943
|
-
else this.transcriptToolProgressSuppressedIds.delete(params.id);
|
|
4944
|
-
this.emitTranscriptToolCallProgress(params);
|
|
4945
|
-
this.toolTranscriptMessages.push(attachCodexMirrorIdentity(this.createToolCallMessage(params), `${this.turnId}:tool:${params.id}:call`));
|
|
4946
|
-
}
|
|
4947
|
-
recordToolTranscriptResult(params) {
|
|
4948
|
-
if (!params.id || !params.name || this.toolTranscriptResultIds.has(params.id)) return;
|
|
4949
|
-
this.toolTranscriptResultIds.add(params.id);
|
|
4950
|
-
this.emitTranscriptToolResultProgress(params);
|
|
4951
|
-
this.toolTranscriptMessages.push(attachCodexMirrorIdentity(this.createToolResultMessage(params), `${this.turnId}:tool:${params.id}:result`));
|
|
4952
|
-
}
|
|
4953
|
-
synthesizeMissingToolResults(params) {
|
|
4954
|
-
if (!params.synthesize) return;
|
|
4955
|
-
const missingTranscriptIds = [...this.toolTranscriptCallIds].filter((id) => !this.toolTranscriptResultIds.has(id));
|
|
4956
|
-
const missingTrajectoryIds = [...this.toolTrajectoryCallIds].filter((id) => !this.toolTrajectoryResultIds.has(id));
|
|
4957
|
-
if (missingTranscriptIds.length === 0 && missingTrajectoryIds.length === 0) return;
|
|
4958
|
-
for (const id of missingTranscriptIds) {
|
|
4959
|
-
const name = this.toolTranscriptNamesById.get(id) ?? this.toolTrajectoryNamesById.get(id);
|
|
4960
|
-
if (!name) continue;
|
|
4961
|
-
this.recordToolTranscriptResult({
|
|
4962
|
-
id,
|
|
4963
|
-
name,
|
|
4964
|
-
text: formatMissingToolResultError({
|
|
4965
|
-
id,
|
|
4966
|
-
name
|
|
4967
|
-
}),
|
|
4968
|
-
isError: true
|
|
4969
|
-
});
|
|
4970
|
-
}
|
|
4971
|
-
for (const id of missingTrajectoryIds) {
|
|
4972
|
-
const name = this.toolTrajectoryNamesById.get(id) ?? this.toolTranscriptNamesById.get(id);
|
|
4973
|
-
if (!name) continue;
|
|
4974
|
-
this.toolTrajectoryResultIds.add(id);
|
|
4975
|
-
const text = formatMissingToolResultError({
|
|
4976
|
-
id,
|
|
4977
|
-
name
|
|
4978
|
-
});
|
|
4979
|
-
this.options.trajectoryRecorder?.recordEvent("tool.result", {
|
|
4980
|
-
threadId: this.threadId,
|
|
4981
|
-
turnId: this.turnId,
|
|
4982
|
-
itemId: id,
|
|
4983
|
-
toolCallId: id,
|
|
4984
|
-
name,
|
|
4985
|
-
status: "failed",
|
|
4986
|
-
isError: true,
|
|
4987
|
-
result: {
|
|
4988
|
-
status: "failed",
|
|
4989
|
-
reason: "missing_tool_result"
|
|
4990
|
-
},
|
|
4991
|
-
output: text
|
|
4992
|
-
});
|
|
4993
|
-
}
|
|
4994
|
-
if (!params.recordPromptError) {
|
|
4995
|
-
const firstMissingId = missingTranscriptIds.find((id) => {
|
|
4996
|
-
const name = this.toolTranscriptNamesById.get(id) ?? this.toolTrajectoryNamesById.get(id);
|
|
4997
|
-
return Boolean(name);
|
|
4998
|
-
}) ?? missingTrajectoryIds.find((id) => {
|
|
4999
|
-
const name = this.toolTrajectoryNamesById.get(id) ?? this.toolTranscriptNamesById.get(id);
|
|
5000
|
-
return Boolean(name);
|
|
5001
|
-
});
|
|
5002
|
-
if (firstMissingId) {
|
|
5003
|
-
const name = this.toolTranscriptNamesById.get(firstMissingId) ?? this.toolTrajectoryNamesById.get(firstMissingId);
|
|
5004
|
-
if (name) {
|
|
5005
|
-
const item = this.toolTrajectoryItemsById.get(firstMissingId);
|
|
5006
|
-
const meta = item ? itemMeta(item, this.toolProgressDetailMode()) : this.toolMetas.get(firstMissingId)?.meta;
|
|
5007
|
-
const actionFingerprint = item ? nativeToolActionFingerprint(item) : void 0;
|
|
5008
|
-
this.lastNativeToolError = {
|
|
5009
|
-
toolName: name,
|
|
5010
|
-
...meta ? { meta } : {},
|
|
5011
|
-
error: formatMissingToolResultError({
|
|
5012
|
-
id: firstMissingId,
|
|
5013
|
-
name
|
|
5014
|
-
}),
|
|
5015
|
-
...item && isMutatingNativeToolItem(item) ? { mutatingAction: true } : {},
|
|
5016
|
-
...actionFingerprint ? { actionFingerprint } : {}
|
|
5017
|
-
};
|
|
5018
|
-
}
|
|
5019
|
-
}
|
|
5020
|
-
return;
|
|
5021
|
-
}
|
|
5022
|
-
const missingCount = new Set([...missingTranscriptIds, ...missingTrajectoryIds]).size;
|
|
5023
|
-
this.synthesizedMissingToolResultError = missingCount === 1 ? MISSING_TOOL_RESULT_ERROR : `${MISSING_TOOL_RESULT_ERROR} missingToolResultCount=${missingCount}`;
|
|
5024
|
-
this.promptErrorSource = this.promptErrorSource ?? "prompt";
|
|
5025
|
-
}
|
|
5026
|
-
emitTranscriptToolCallProgress(params) {
|
|
5027
|
-
if (!shouldEmitTranscriptToolProgress(params.name, params.arguments)) return;
|
|
5028
|
-
this.transcriptToolProgressCallIds.add(params.id);
|
|
5029
|
-
const args = normalizeToolTranscriptArguments(params.arguments);
|
|
5030
|
-
const meta = inferToolMetaFromArgs(params.name, args, { detailMode: this.toolProgressDetailMode() });
|
|
5031
|
-
if (!this.params.onToolResult || !this.shouldEmitToolResult() || this.toolResultSummaryItemIds.has(params.id) || this.toolResultOutputStreamedItemIds.has(params.id)) return;
|
|
5032
|
-
this.toolResultSummaryItemIds.add(params.id);
|
|
5033
|
-
this.emitToolResultMessage({
|
|
5034
|
-
itemId: params.id,
|
|
5035
|
-
text: formatToolSummary(params.name, meta)
|
|
5036
|
-
});
|
|
5037
|
-
}
|
|
5038
|
-
emitTranscriptToolResultProgress(params) {
|
|
5039
|
-
if (this.transcriptToolProgressSuppressedIds.has(params.id) || !shouldEmitTranscriptToolProgress(params.name, this.toolTranscriptArgumentsById.get(params.id))) return;
|
|
5040
|
-
if (!this.transcriptToolProgressCallIds.has(params.id)) this.emitTranscriptToolCallProgress({
|
|
5041
|
-
id: params.id,
|
|
5042
|
-
name: params.name,
|
|
5043
|
-
arguments: {}
|
|
5044
|
-
});
|
|
5045
|
-
if (!this.params.onToolResult || !this.shouldEmitToolOutput() || this.toolResultOutputItemIds.has(params.id) || this.toolResultOutputStreamedItemIds.has(params.id)) return;
|
|
5046
|
-
const text = params.text?.trim();
|
|
5047
|
-
if (!text) return;
|
|
5048
|
-
this.emitToolResultMessage({
|
|
5049
|
-
itemId: params.id,
|
|
5050
|
-
text: formatToolOutput(params.name, void 0, text),
|
|
5051
|
-
finalOutput: true,
|
|
5052
|
-
isError: params.isError
|
|
5053
|
-
});
|
|
5054
|
-
}
|
|
5055
|
-
formatCodexErrorMessage(params) {
|
|
5056
|
-
const error = isJsonObject(params.error) ? params.error : void 0;
|
|
5057
|
-
return formatCodexUsageLimitErrorMessage({
|
|
5058
|
-
message: error ? readString$5(error, "message") : void 0,
|
|
5059
|
-
codexErrorInfo: error?.codexErrorInfo,
|
|
5060
|
-
rateLimits: this.latestRateLimits ?? readRecentCodexRateLimits()
|
|
5061
|
-
}) ?? readCodexErrorNotificationMessage(params);
|
|
5062
|
-
}
|
|
5063
|
-
emitAgentEvent(event) {
|
|
5064
|
-
try {
|
|
5065
|
-
emitAgentEvent({
|
|
5066
|
-
runId: this.params.runId,
|
|
5067
|
-
stream: event.stream,
|
|
5068
|
-
data: event.data,
|
|
5069
|
-
...this.params.sessionKey ? { sessionKey: this.params.sessionKey } : {}
|
|
5070
|
-
});
|
|
5071
|
-
} catch (error) {
|
|
5072
|
-
embeddedAgentLog.debug("codex app-server global agent event emit failed", { error });
|
|
5073
|
-
}
|
|
5074
|
-
try {
|
|
5075
|
-
const maybePromise = this.params.onAgentEvent?.(event);
|
|
5076
|
-
Promise.resolve(maybePromise).catch((error) => {
|
|
5077
|
-
embeddedAgentLog.debug("codex app-server agent event handler rejected", { error });
|
|
5078
|
-
});
|
|
5079
|
-
} catch (error) {
|
|
5080
|
-
embeddedAgentLog.debug("codex app-server agent event handler threw", { error });
|
|
5081
|
-
}
|
|
5082
|
-
}
|
|
5083
|
-
collectAssistantTexts() {
|
|
5084
|
-
const finalText = this.resolveFinalAssistantText();
|
|
5085
|
-
return finalText ? [finalText] : [];
|
|
5086
|
-
}
|
|
5087
|
-
hasAssistantItemTextForSynthesis() {
|
|
5088
|
-
for (let i = this.assistantItemOrder.length - 1; i >= 0; i -= 1) {
|
|
5089
|
-
const itemId = this.assistantItemOrder[i];
|
|
5090
|
-
if (!itemId) continue;
|
|
5091
|
-
if (this.assistantPhaseByItem.get(itemId) === "commentary") continue;
|
|
5092
|
-
const text = this.assistantTextByItem.get(itemId);
|
|
5093
|
-
if (text && text.length > 0) return true;
|
|
5094
|
-
}
|
|
5095
|
-
return false;
|
|
5096
|
-
}
|
|
5097
|
-
resolveFinalAssistantText() {
|
|
5098
|
-
return this.resolveFinalAssistantTextItem()?.text;
|
|
5099
|
-
}
|
|
5100
|
-
resolveFinalAssistantTextItem() {
|
|
5101
|
-
for (let i = this.assistantItemOrder.length - 1; i >= 0; i -= 1) {
|
|
5102
|
-
const itemId = this.assistantItemOrder[i];
|
|
5103
|
-
if (!itemId) continue;
|
|
5104
|
-
const text = this.assistantTextByItem.get(itemId)?.trim();
|
|
5105
|
-
if (this.assistantPhaseByItem.get(itemId) === "commentary") continue;
|
|
5106
|
-
if (text && !this.toolProgressTexts.has(text)) return {
|
|
5107
|
-
itemId,
|
|
5108
|
-
text
|
|
5109
|
-
};
|
|
5110
|
-
}
|
|
5111
|
-
}
|
|
5112
|
-
rememberAssistantItem(itemId) {
|
|
5113
|
-
if (!itemId || this.assistantItemOrder.includes(itemId)) return;
|
|
5114
|
-
this.assistantItemOrder.push(itemId);
|
|
5115
|
-
}
|
|
5116
|
-
createCurrentAttemptAssistantMessage() {
|
|
5117
|
-
for (let i = this.assistantItemOrder.length - 1; i >= 0; i -= 1) {
|
|
5118
|
-
const itemId = this.assistantItemOrder[i];
|
|
5119
|
-
if (!itemId || this.isCommentaryAssistantItem(itemId) || !this.assistantTextByItem.has(itemId)) continue;
|
|
5120
|
-
const text = this.assistantTextByItem.get(itemId) ?? "";
|
|
5121
|
-
const normalizedText = text.trim();
|
|
5122
|
-
if (normalizedText && this.toolProgressTexts.has(normalizedText)) continue;
|
|
5123
|
-
return this.createAssistantMessage(text);
|
|
5124
|
-
}
|
|
5125
|
-
}
|
|
5126
|
-
async readMirroredSessionMessages() {
|
|
5127
|
-
return await readCodexMirroredSessionHistoryMessages({
|
|
5128
|
-
agentId: this.params.agentId,
|
|
5129
|
-
sessionFile: this.params.sessionFile,
|
|
5130
|
-
sessionId: this.params.sessionId,
|
|
5131
|
-
sessionKey: this.params.sessionKey
|
|
5132
|
-
}) ?? [];
|
|
5133
|
-
}
|
|
5134
|
-
createAssistantMessage(text) {
|
|
5135
|
-
const attribution = resolveCodexLocalRuntimeAttribution(this.params);
|
|
5136
|
-
const usage = this.tokenUsage ? {
|
|
5137
|
-
input: this.tokenUsage.input ?? 0,
|
|
5138
|
-
output: this.tokenUsage.output ?? 0,
|
|
5139
|
-
cacheRead: this.tokenUsage.cacheRead ?? 0,
|
|
5140
|
-
cacheWrite: this.tokenUsage.cacheWrite ?? 0,
|
|
5141
|
-
totalTokens: this.tokenUsage.total ?? (this.tokenUsage.input ?? 0) + (this.tokenUsage.output ?? 0) + (this.tokenUsage.cacheRead ?? 0) + (this.tokenUsage.cacheWrite ?? 0),
|
|
5142
|
-
cost: ZERO_USAGE.cost
|
|
5143
|
-
} : ZERO_USAGE;
|
|
5144
|
-
return {
|
|
5145
|
-
role: "assistant",
|
|
5146
|
-
content: [{
|
|
5147
|
-
type: "text",
|
|
5148
|
-
text
|
|
5149
|
-
}],
|
|
5150
|
-
api: attribution.api ?? "openai-chatgpt-responses",
|
|
5151
|
-
provider: attribution.provider,
|
|
5152
|
-
model: this.params.modelId,
|
|
5153
|
-
usage,
|
|
5154
|
-
stopReason: this.aborted ? "aborted" : this.promptError ? "error" : "stop",
|
|
5155
|
-
errorMessage: this.promptError ? formatErrorMessage(this.promptError) : void 0,
|
|
5156
|
-
timestamp: Date.now()
|
|
5157
|
-
};
|
|
5158
|
-
}
|
|
5159
|
-
createAssistantMirrorMessage(title, text) {
|
|
5160
|
-
const attribution = resolveCodexLocalRuntimeAttribution(this.params);
|
|
5161
|
-
return {
|
|
5162
|
-
role: "assistant",
|
|
5163
|
-
content: [{
|
|
5164
|
-
type: "text",
|
|
5165
|
-
text: `${title}:\n${text}`
|
|
5166
|
-
}],
|
|
5167
|
-
api: attribution.api ?? "openai-chatgpt-responses",
|
|
5168
|
-
provider: attribution.provider,
|
|
5169
|
-
model: this.params.modelId,
|
|
5170
|
-
usage: ZERO_USAGE,
|
|
5171
|
-
stopReason: "stop",
|
|
5172
|
-
timestamp: Date.now()
|
|
5173
|
-
};
|
|
5174
|
-
}
|
|
5175
|
-
createToolCallMessage(params) {
|
|
5176
|
-
const args = normalizeToolTranscriptArguments(params.arguments);
|
|
5177
|
-
const attribution = resolveCodexLocalRuntimeAttribution(this.params);
|
|
5178
|
-
return {
|
|
5179
|
-
role: "assistant",
|
|
5180
|
-
content: [{
|
|
5181
|
-
type: "toolCall",
|
|
5182
|
-
id: params.id,
|
|
5183
|
-
name: params.name,
|
|
5184
|
-
arguments: args,
|
|
5185
|
-
input: args
|
|
5186
|
-
}],
|
|
5187
|
-
api: attribution.api ?? "openai-chatgpt-responses",
|
|
5188
|
-
provider: attribution.provider,
|
|
5189
|
-
model: this.params.modelId,
|
|
5190
|
-
usage: ZERO_USAGE,
|
|
5191
|
-
stopReason: "toolUse",
|
|
5192
|
-
timestamp: Date.now()
|
|
5193
|
-
};
|
|
5194
|
-
}
|
|
5195
|
-
createToolResultMessage(params) {
|
|
5196
|
-
const text = truncateToolTranscriptText(params.text?.trim() || toolResultStatusText(params));
|
|
5197
|
-
return {
|
|
5198
|
-
role: "toolResult",
|
|
5199
|
-
toolCallId: params.id,
|
|
5200
|
-
toolName: params.name,
|
|
5201
|
-
isError: params.isError,
|
|
5202
|
-
content: [{
|
|
5203
|
-
type: "toolResult",
|
|
5204
|
-
id: params.id,
|
|
5205
|
-
name: params.name,
|
|
5206
|
-
toolName: params.name,
|
|
5207
|
-
toolCallId: params.id,
|
|
5208
|
-
toolUseId: params.id,
|
|
5209
|
-
tool_use_id: params.id,
|
|
5210
|
-
content: text,
|
|
5211
|
-
text
|
|
5212
|
-
}],
|
|
5213
|
-
timestamp: Date.now()
|
|
5214
|
-
};
|
|
5215
|
-
}
|
|
5216
|
-
isNotificationForTurn(params) {
|
|
5217
|
-
const threadId = readCodexNotificationThreadId(params);
|
|
5218
|
-
const turnId = readNotificationTurnId(params);
|
|
5219
|
-
return threadId === this.threadId && turnId === this.turnId;
|
|
5220
|
-
}
|
|
5221
|
-
isHookNotificationForCurrentThread(params) {
|
|
5222
|
-
const threadId = readString$5(params, "threadId");
|
|
5223
|
-
const turnId = params.turnId;
|
|
5224
|
-
return threadId === this.threadId && (turnId === this.turnId || turnId === null);
|
|
5225
|
-
}
|
|
5226
|
-
};
|
|
5227
|
-
function isHookNotificationMethod(method) {
|
|
5228
|
-
return method === "hook/started" || method === "hook/completed";
|
|
5229
|
-
}
|
|
5230
|
-
function readNotificationTurnId(record) {
|
|
5231
|
-
return readCodexNotificationTurnId(record);
|
|
5232
|
-
}
|
|
5233
|
-
function readString$5(record, key) {
|
|
5234
|
-
const value = record[key];
|
|
5235
|
-
return typeof value === "string" ? value : void 0;
|
|
5236
|
-
}
|
|
5237
|
-
function estimateBase64DecodedBytes(base64) {
|
|
5238
|
-
let nonWhitespaceLength = 0;
|
|
5239
|
-
let previousCode = -1;
|
|
5240
|
-
let lastCode = -1;
|
|
5241
|
-
for (let i = 0; i < base64.length; i += 1) {
|
|
5242
|
-
const code = base64.charCodeAt(i);
|
|
5243
|
-
if (isBase64WhitespaceCode(code)) continue;
|
|
5244
|
-
nonWhitespaceLength += 1;
|
|
5245
|
-
previousCode = lastCode;
|
|
5246
|
-
lastCode = code;
|
|
5247
|
-
}
|
|
5248
|
-
if (nonWhitespaceLength === 0) return;
|
|
5249
|
-
const equalsCode = "=".charCodeAt(0);
|
|
5250
|
-
const padding = lastCode === equalsCode ? previousCode === equalsCode ? 2 : 1 : 0;
|
|
5251
|
-
return Math.max(0, Math.floor(nonWhitespaceLength * 3 / 4) - padding);
|
|
5252
|
-
}
|
|
5253
|
-
function isBase64WhitespaceCode(code) {
|
|
5254
|
-
return code === 32 || code === 9 || code === 10 || code === 13;
|
|
5255
|
-
}
|
|
5256
|
-
function resolveGeneratedImageMaxBytes(config) {
|
|
5257
|
-
const configured = config?.agents?.defaults?.mediaMaxMb;
|
|
5258
|
-
if (typeof configured === "number" && Number.isFinite(configured) && configured > 0) return Math.floor(configured * BYTES_PER_MB);
|
|
5259
|
-
return DEFAULT_GENERATED_IMAGE_MAX_BYTES;
|
|
5260
|
-
}
|
|
5261
|
-
function normalizeNonEmptyString(value) {
|
|
5262
|
-
if (typeof value !== "string") return;
|
|
5263
|
-
return value.trim() || void 0;
|
|
5264
|
-
}
|
|
5265
|
-
function readNonEmptyString(record, key) {
|
|
5266
|
-
return normalizeNonEmptyString(record[key]);
|
|
5267
|
-
}
|
|
5268
|
-
function readNonEmptyStringArray(record, key) {
|
|
5269
|
-
const value = record[key];
|
|
5270
|
-
if (!Array.isArray(value)) return [];
|
|
5271
|
-
const entries = [];
|
|
5272
|
-
for (const entry of value) {
|
|
5273
|
-
const normalized = normalizeNonEmptyString(entry);
|
|
5274
|
-
if (normalized) entries.push(normalized);
|
|
5275
|
-
}
|
|
5276
|
-
return entries;
|
|
5277
|
-
}
|
|
5278
|
-
function readNullableString$1(record, key) {
|
|
5279
|
-
const value = record[key];
|
|
5280
|
-
if (value === null) return null;
|
|
5281
|
-
return typeof value === "string" ? value : void 0;
|
|
5282
|
-
}
|
|
5283
|
-
function readNumber$1(record, key) {
|
|
5284
|
-
const value = record[key];
|
|
5285
|
-
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
5286
|
-
}
|
|
5287
|
-
function resolveStartedAtFromDurationMs(durationMs) {
|
|
5288
|
-
if (typeof durationMs !== "number" || !Number.isFinite(durationMs)) return;
|
|
5289
|
-
return asDateTimestampMs(Date.now() - Math.max(0, durationMs));
|
|
5290
|
-
}
|
|
5291
|
-
function readNonNegativeInteger(record, key) {
|
|
5292
|
-
const value = readNumber$1(record, key);
|
|
5293
|
-
return value !== void 0 && Number.isInteger(value) && value >= 0 ? value : void 0;
|
|
5294
|
-
}
|
|
5295
|
-
function readBoolean(record, key) {
|
|
5296
|
-
const value = record[key];
|
|
5297
|
-
return typeof value === "boolean" ? value : void 0;
|
|
5298
|
-
}
|
|
5299
|
-
function readBooleanAlias(record, keys) {
|
|
5300
|
-
for (const key of keys) {
|
|
5301
|
-
const value = readBoolean(record, key);
|
|
5302
|
-
if (value !== void 0) return value;
|
|
5303
|
-
}
|
|
5304
|
-
}
|
|
5305
|
-
function readCodexErrorNotificationMessage(record) {
|
|
5306
|
-
const error = record.error;
|
|
5307
|
-
if (isJsonObject(error)) return readString$5(error, "message") ?? readString$5(error, "error");
|
|
5308
|
-
return readString$5(record, "message");
|
|
5309
|
-
}
|
|
5310
|
-
function readHookOutputEntries(value) {
|
|
5311
|
-
if (!Array.isArray(value)) return [];
|
|
5312
|
-
return value.flatMap((entry) => {
|
|
5313
|
-
if (!isJsonObject(entry)) return [];
|
|
5314
|
-
const text = readString$5(entry, "text");
|
|
5315
|
-
if (!text) return [];
|
|
5316
|
-
const kind = readString$5(entry, "kind");
|
|
5317
|
-
return [{
|
|
5318
|
-
...kind ? { kind } : {},
|
|
5319
|
-
text
|
|
5320
|
-
}];
|
|
5321
|
-
});
|
|
5322
|
-
}
|
|
5323
|
-
function readFirstJsonObject(record, keys) {
|
|
5324
|
-
for (const key of keys) {
|
|
5325
|
-
const value = record[key];
|
|
5326
|
-
if (isJsonObject(value)) return value;
|
|
5327
|
-
}
|
|
5328
|
-
}
|
|
5329
|
-
function readNumberAlias(record, keys) {
|
|
5330
|
-
for (const key of keys) {
|
|
5331
|
-
const value = readNumber$1(record, key);
|
|
5332
|
-
if (value !== void 0) return value;
|
|
5333
|
-
}
|
|
5334
|
-
}
|
|
5335
|
-
function normalizeCodexTokenUsage(record) {
|
|
5336
|
-
const promptTotalInput = readNumberAlias(record, CODEX_PROMPT_TOTAL_INPUT_KEYS);
|
|
5337
|
-
const cacheRead = readNumberAlias(record, [
|
|
5338
|
-
"cachedInputTokens",
|
|
5339
|
-
"cached_input_tokens",
|
|
5340
|
-
"cacheRead",
|
|
5341
|
-
"cache_read",
|
|
5342
|
-
"cache_read_input_tokens",
|
|
5343
|
-
"cached_tokens"
|
|
5344
|
-
]);
|
|
5345
|
-
return normalizeUsage({
|
|
5346
|
-
input: promptTotalInput !== void 0 && cacheRead !== void 0 ? Math.max(0, promptTotalInput - cacheRead) : promptTotalInput ?? readNumber$1(record, "input"),
|
|
5347
|
-
output: readNumberAlias(record, [
|
|
5348
|
-
"outputTokens",
|
|
5349
|
-
"output_tokens",
|
|
5350
|
-
"output"
|
|
5351
|
-
]),
|
|
5352
|
-
cacheRead,
|
|
5353
|
-
cacheWrite: readNumberAlias(record, [
|
|
5354
|
-
"cacheWrite",
|
|
5355
|
-
"cache_write",
|
|
5356
|
-
"cacheCreationInputTokens",
|
|
5357
|
-
"cache_creation_input_tokens"
|
|
5358
|
-
]),
|
|
5359
|
-
total: readNumberAlias(record, [
|
|
5360
|
-
"totalTokens",
|
|
5361
|
-
"total_tokens",
|
|
5362
|
-
"total"
|
|
5363
|
-
])
|
|
5364
|
-
});
|
|
5365
|
-
}
|
|
5366
|
-
function splitPlanText(text) {
|
|
5367
|
-
return text.split(/\r?\n/).map((line) => line.trim().replace(/^[-*]\s+/, "")).filter((line) => line.length > 0);
|
|
5368
|
-
}
|
|
5369
|
-
function collectTextValues(map) {
|
|
5370
|
-
return [...map.values()].filter((text) => text.trim().length > 0);
|
|
5371
|
-
}
|
|
5372
|
-
function collectReasoningTextValues(groups, itemOrder) {
|
|
5373
|
-
return [...groups.values()].toSorted((left, right) => {
|
|
5374
|
-
const itemDelta = (itemOrder.get(left.itemId) ?? Number.MAX_SAFE_INTEGER) - (itemOrder.get(right.itemId) ?? Number.MAX_SAFE_INTEGER);
|
|
5375
|
-
if (itemDelta !== 0) return itemDelta;
|
|
5376
|
-
const methodDelta = reasoningMethodOrder(left.method) - reasoningMethodOrder(right.method);
|
|
5377
|
-
return methodDelta !== 0 ? methodDelta : left.index - right.index;
|
|
5378
|
-
}).map((group) => group.text).filter((text) => text.trim().length > 0);
|
|
5379
|
-
}
|
|
5380
|
-
function reasoningMethodOrder(method) {
|
|
5381
|
-
return method === "item/reasoning/summaryTextDelta" ? 0 : 1;
|
|
5382
|
-
}
|
|
5383
|
-
function extractRawAssistantText(item) {
|
|
5384
|
-
return (Array.isArray(item.content) ? item.content : []).flatMap((entry) => {
|
|
5385
|
-
if (!isJsonObject(entry)) return [];
|
|
5386
|
-
const type = readString$5(entry, "type");
|
|
5387
|
-
if (type !== "output_text" && type !== "text") return [];
|
|
5388
|
-
const value = readString$5(entry, "text");
|
|
5389
|
-
return value ? [value] : [];
|
|
5390
|
-
}).join("").trim() || void 0;
|
|
5391
|
-
}
|
|
5392
|
-
function itemKind(item) {
|
|
5393
|
-
switch (item.type) {
|
|
5394
|
-
case "dynamicToolCall":
|
|
5395
|
-
case "mcpToolCall": return "tool";
|
|
5396
|
-
case "commandExecution": return "command";
|
|
5397
|
-
case "fileChange": return "patch";
|
|
5398
|
-
case "webSearch": return "search";
|
|
5399
|
-
case "reasoning":
|
|
5400
|
-
case "contextCompaction": return "analysis";
|
|
5401
|
-
default: return;
|
|
5402
|
-
}
|
|
5403
|
-
}
|
|
5404
|
-
function itemTitle(item) {
|
|
5405
|
-
switch (item.type) {
|
|
5406
|
-
case "commandExecution": return "Command";
|
|
5407
|
-
case "fileChange": return "File change";
|
|
5408
|
-
case "mcpToolCall": return "MCP tool";
|
|
5409
|
-
case "dynamicToolCall": return "Tool";
|
|
5410
|
-
case "webSearch": return "Web search";
|
|
5411
|
-
case "contextCompaction": return "Context compaction";
|
|
5412
|
-
case "reasoning": return "Reasoning";
|
|
5413
|
-
default: return item.type;
|
|
5414
|
-
}
|
|
5415
|
-
}
|
|
5416
|
-
function itemStatus(item) {
|
|
5417
|
-
const status = readItemString(item, "status");
|
|
5418
|
-
if (status === "failed") return "failed";
|
|
5419
|
-
if (status === "declined") return "blocked";
|
|
5420
|
-
if (status === "inProgress" || status === "running") return "running";
|
|
5421
|
-
return "completed";
|
|
5422
|
-
}
|
|
5423
|
-
function formatMissingToolResultError(params) {
|
|
5424
|
-
return `${MISSING_TOOL_RESULT_ERROR} toolCallId=${params.id}; toolName=${params.name}`;
|
|
5425
|
-
}
|
|
5426
|
-
function isNonSuccessItemStatus(status) {
|
|
5427
|
-
return status === "failed" || status === "blocked";
|
|
5428
|
-
}
|
|
5429
|
-
function itemName(item) {
|
|
5430
|
-
if (item.type === "dynamicToolCall" && typeof item.tool === "string") return item.tool;
|
|
5431
|
-
if (item.type === "mcpToolCall" && typeof item.tool === "string") {
|
|
5432
|
-
const server = typeof item.server === "string" ? item.server : void 0;
|
|
5433
|
-
return server ? `${server}.${item.tool}` : item.tool;
|
|
5434
|
-
}
|
|
5435
|
-
if (item.type === "commandExecution") return "bash";
|
|
5436
|
-
if (item.type === "fileChange") return "apply_patch";
|
|
5437
|
-
if (item.type === "webSearch") return "web_search";
|
|
5438
|
-
}
|
|
5439
|
-
function isSideEffectingNativeToolItem(item) {
|
|
5440
|
-
return itemStatus(item) !== "blocked" && (isMutatingNativeToolItem(item) || item.type === "mcpToolCall");
|
|
5441
|
-
}
|
|
5442
|
-
function shouldSynthesizeToolProgressForItem(item) {
|
|
5443
|
-
switch (item.type) {
|
|
5444
|
-
case "commandExecution":
|
|
5445
|
-
case "fileChange":
|
|
5446
|
-
case "webSearch":
|
|
5447
|
-
case "mcpToolCall": return true;
|
|
5448
|
-
default: return false;
|
|
5449
|
-
}
|
|
5450
|
-
}
|
|
5451
|
-
function shouldRecordNativeToolTranscript(item) {
|
|
5452
|
-
return shouldSynthesizeToolProgressForItem(item) && item.type !== "webSearch";
|
|
5453
|
-
}
|
|
5454
|
-
function isMutatingNativeToolItem(item) {
|
|
5455
|
-
if (item.type === "commandExecution") return true;
|
|
5456
|
-
return item.type === "fileChange" || item.type === "collabAgentToolCall" || item.type === "imageGeneration";
|
|
5457
|
-
}
|
|
5458
|
-
function shouldClearTerminalPresentationForNativeItem(item) {
|
|
5459
|
-
switch (item.type) {
|
|
5460
|
-
case "collabAgentToolCall":
|
|
5461
|
-
case "commandExecution":
|
|
5462
|
-
case "fileChange":
|
|
5463
|
-
case "imageGeneration":
|
|
5464
|
-
case "imageView":
|
|
5465
|
-
case "mcpToolCall":
|
|
5466
|
-
case "webSearch": return true;
|
|
5467
|
-
default: return false;
|
|
5468
|
-
}
|
|
5469
|
-
}
|
|
5470
|
-
function nativeToolActionFingerprint(item) {
|
|
5471
|
-
if (item.type === "commandExecution" && typeof item.command === "string") return JSON.stringify({
|
|
5472
|
-
type: item.type,
|
|
5473
|
-
command: item.command,
|
|
5474
|
-
cwd: typeof item.cwd === "string" ? item.cwd : ""
|
|
5475
|
-
});
|
|
5476
|
-
if (item.type === "fileChange") return JSON.stringify({
|
|
5477
|
-
type: item.type,
|
|
5478
|
-
changes: itemFileChanges(item)
|
|
5479
|
-
});
|
|
5480
|
-
}
|
|
5481
|
-
function isNativePostToolUseRelayItem(item) {
|
|
5482
|
-
switch (item.type) {
|
|
5483
|
-
case "commandExecution":
|
|
5484
|
-
case "fileChange":
|
|
5485
|
-
case "mcpToolCall": return true;
|
|
5486
|
-
default: return false;
|
|
5487
|
-
}
|
|
5488
|
-
}
|
|
5489
|
-
function shouldSuppressChannelProgressForItem(item) {
|
|
5490
|
-
if (shouldSynthesizeToolProgressForItem(item)) return true;
|
|
5491
|
-
return item.type === "dynamicToolCall";
|
|
5492
|
-
}
|
|
5493
|
-
function itemToolArgs(item) {
|
|
5494
|
-
if (item.type === "commandExecution") return sanitizeCodexAgentEventRecord({
|
|
5495
|
-
command: item.command,
|
|
5496
|
-
...typeof item.cwd === "string" ? { cwd: item.cwd } : {}
|
|
5497
|
-
});
|
|
5498
|
-
if (item.type === "fileChange") return sanitizeCodexAgentEventRecord({ changes: itemFileChanges(item) });
|
|
5499
|
-
if (item.type === "webSearch") return webSearchToolArgs(item);
|
|
5500
|
-
if (item.type === "dynamicToolCall" || item.type === "mcpToolCall") return sanitizeCodexToolArguments(item.arguments);
|
|
5501
|
-
}
|
|
5502
|
-
function webSearchToolArgs(item) {
|
|
5503
|
-
const action = isJsonObject(item.action) ? item.action : void 0;
|
|
5504
|
-
const actionType = action ? readNonEmptyString(action, "type") : void 0;
|
|
5505
|
-
const queries = action && actionType === "search" ? readNonEmptyStringArray(action, "queries") : [];
|
|
5506
|
-
const query = normalizeNonEmptyString(item.query) ?? (action && actionType === "search" ? readNonEmptyString(action, "query") : void 0) ?? queries[0];
|
|
5507
|
-
const url = action ? readNonEmptyString(action, "url") : void 0;
|
|
5508
|
-
const pattern = action ? readNonEmptyString(action, "pattern") : void 0;
|
|
5509
|
-
const args = {};
|
|
5510
|
-
if (query) args.query = query;
|
|
5511
|
-
if (queries.length > 0) args.queries = queries;
|
|
5512
|
-
if (actionType && actionType !== "search") args.action = actionType;
|
|
5513
|
-
if (url) args.url = url;
|
|
5514
|
-
if (pattern) args.pattern = pattern;
|
|
5515
|
-
if (!query && !url && !pattern) args.queryUnavailable = true;
|
|
5516
|
-
return sanitizeCodexAgentEventRecord(args);
|
|
5517
|
-
}
|
|
5518
|
-
function itemToolResult(item) {
|
|
5519
|
-
if (item.type === "commandExecution") return { result: sanitizeCodexAgentEventRecord({
|
|
5520
|
-
status: item.status,
|
|
5521
|
-
exitCode: item.exitCode,
|
|
5522
|
-
durationMs: item.durationMs
|
|
5523
|
-
}) };
|
|
5524
|
-
if (item.type === "fileChange") return { result: sanitizeCodexAgentEventRecord({
|
|
5525
|
-
status: item.status,
|
|
5526
|
-
changes: itemFileChanges(item)
|
|
5527
|
-
}) };
|
|
5528
|
-
if (item.type === "mcpToolCall") return { result: sanitizeCodexAgentEventRecord({
|
|
5529
|
-
status: item.status,
|
|
5530
|
-
durationMs: item.durationMs,
|
|
5531
|
-
...item.error ? { error: item.error } : {},
|
|
5532
|
-
...item.result ? { result: item.result } : {}
|
|
5533
|
-
}) };
|
|
5534
|
-
if (item.type === "webSearch") return { result: webSearchToolResult(item) };
|
|
5535
|
-
return {};
|
|
5536
|
-
}
|
|
5537
|
-
function webSearchToolResult(item) {
|
|
5538
|
-
return sanitizeCodexAgentEventRecord({
|
|
5539
|
-
status: itemStatus(item),
|
|
5540
|
-
...typeof item.durationMs === "number" ? { durationMs: item.durationMs } : {},
|
|
5541
|
-
...webSearchToolArgs(item)
|
|
5542
|
-
});
|
|
5543
|
-
}
|
|
5544
|
-
function itemFileChanges(item) {
|
|
5545
|
-
return Array.isArray(item.changes) ? item.changes.map((change) => ({
|
|
5546
|
-
path: change.path,
|
|
5547
|
-
kind: change.kind
|
|
5548
|
-
})) : [];
|
|
5549
|
-
}
|
|
5550
|
-
function itemToolError(item, status, outputTextByItem) {
|
|
5551
|
-
if (status === "blocked") return "codex native tool blocked";
|
|
5552
|
-
if (status !== "failed") return;
|
|
5553
|
-
return itemOutputText(item, outputTextByItem) ?? "codex native tool failed";
|
|
5554
|
-
}
|
|
5555
|
-
function itemMeta(item, detailMode = "explain") {
|
|
5556
|
-
if (item.type === "commandExecution" && typeof item.command === "string") return inferToolMetaFromArgs("exec", {
|
|
5557
|
-
command: item.command,
|
|
5558
|
-
cwd: typeof item.cwd === "string" ? item.cwd : void 0
|
|
5559
|
-
}, { detailMode });
|
|
5560
|
-
if (item.type === "webSearch") return inferToolMetaFromArgs("web_search", webSearchToolArgs(item), { detailMode });
|
|
5561
|
-
const toolName = itemName(item);
|
|
5562
|
-
if ((item.type === "dynamicToolCall" || item.type === "mcpToolCall") && toolName) return inferToolMetaFromArgs(toolName, item.arguments, { detailMode });
|
|
5563
|
-
}
|
|
5564
|
-
function itemOutputText(item, outputTextByItem) {
|
|
5565
|
-
if (item.type === "commandExecution") return item.aggregatedOutput?.trim() || outputTextByItem?.get(item.id)?.trim() || void 0;
|
|
5566
|
-
if (item.type === "dynamicToolCall") return collectDynamicToolContentText(item.contentItems).trim() || void 0;
|
|
5567
|
-
if (item.type === "mcpToolCall") {
|
|
5568
|
-
if (item.error) return stringifyJsonValue(item.error);
|
|
5569
|
-
return item.result ? stringifyJsonValue(item.result) : void 0;
|
|
5570
|
-
}
|
|
5571
|
-
}
|
|
5572
|
-
function itemTranscriptResultText(item, outputTextByItem) {
|
|
5573
|
-
const output = itemOutputText(item, outputTextByItem);
|
|
5574
|
-
if (output) return output;
|
|
5575
|
-
const result = itemToolResult(item).result;
|
|
5576
|
-
return result ? stringifyJsonValue(result) : itemStatus(item);
|
|
5577
|
-
}
|
|
5578
|
-
function appendToolOutputDeltaText(outputTextByItem, itemId, delta) {
|
|
5579
|
-
const current = outputTextByItem.get(itemId) ?? "";
|
|
5580
|
-
if (current.length >= TOOL_TRANSCRIPT_OUTPUT_MAX_CHARS) return;
|
|
5581
|
-
const remaining = TOOL_TRANSCRIPT_OUTPUT_MAX_CHARS - current.length;
|
|
5582
|
-
const next = current + (delta.length > remaining ? delta.slice(0, remaining) : delta);
|
|
5583
|
-
outputTextByItem.set(itemId, next);
|
|
5584
|
-
}
|
|
5585
|
-
function normalizeToolTranscriptArguments(value) {
|
|
5586
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
|
|
5587
|
-
return value;
|
|
5588
|
-
}
|
|
5589
|
-
function collectDynamicToolContentText(contentItems) {
|
|
5590
|
-
if (!Array.isArray(contentItems)) return "";
|
|
5591
|
-
return contentItems.flatMap((entry) => {
|
|
5592
|
-
if (!isJsonObject(entry)) return [];
|
|
5593
|
-
const text = readString$5(entry, "text");
|
|
5594
|
-
return text ? [text] : [];
|
|
5595
|
-
}).join("\n");
|
|
5596
|
-
}
|
|
5597
|
-
function truncateToolTranscriptText(text) {
|
|
5598
|
-
if (text.length <= TOOL_TRANSCRIPT_OUTPUT_MAX_CHARS) return text;
|
|
5599
|
-
return `${text.slice(0, TOOL_TRANSCRIPT_OUTPUT_MAX_CHARS)}\n...(truncated)...`;
|
|
5600
|
-
}
|
|
5601
|
-
function toolResultStatusText(params) {
|
|
5602
|
-
return params.isError ? `${params.name} failed` : `${params.name} completed`;
|
|
5603
|
-
}
|
|
5604
|
-
function stringifyJsonValue(value) {
|
|
5605
|
-
try {
|
|
5606
|
-
return JSON.stringify(value, null, 2);
|
|
5607
|
-
} catch {
|
|
5608
|
-
return;
|
|
5609
|
-
}
|
|
5610
|
-
}
|
|
5611
|
-
function formatToolSummary(toolName, meta) {
|
|
5612
|
-
const trimmedMeta = meta?.trim();
|
|
5613
|
-
return formatToolAggregate(toolName, trimmedMeta ? [trimmedMeta] : void 0, { markdown: true });
|
|
5614
|
-
}
|
|
5615
|
-
function formatToolOutput(toolName, meta, output) {
|
|
5616
|
-
const formattedOutput = formatToolProgressOutput(output);
|
|
5617
|
-
if (!formattedOutput) return formatToolSummary(toolName, meta);
|
|
5618
|
-
const fence = markdownFenceForText(formattedOutput);
|
|
5619
|
-
return `${formatToolSummary(toolName, meta)}\n${fence}txt\n${formattedOutput}\n${fence}`;
|
|
5620
|
-
}
|
|
5621
|
-
function markdownFenceForText(text) {
|
|
5622
|
-
return "`".repeat(Math.max(3, longestBacktickRun(text) + 1));
|
|
5623
|
-
}
|
|
5624
|
-
function longestBacktickRun(value) {
|
|
5625
|
-
let longest = 0;
|
|
5626
|
-
let current = 0;
|
|
5627
|
-
for (const char of value) {
|
|
5628
|
-
if (char === "`") {
|
|
5629
|
-
current += 1;
|
|
5630
|
-
longest = Math.max(longest, current);
|
|
5631
|
-
continue;
|
|
3308
|
+
function fireTerminalIdleTimeout() {
|
|
3309
|
+
if (params.isCompleted() || params.isTerminalTurnNotificationQueued() || params.signal.aborted || !terminalIdleWatchArmed || params.getActiveAppServerTurnRequests() > 0) return;
|
|
3310
|
+
const idleMs = Math.max(0, Date.now() - completionLastActivityAt);
|
|
3311
|
+
if (idleMs < turnTerminalIdleTimeoutMs) {
|
|
3312
|
+
scheduleTerminalIdleWatch();
|
|
3313
|
+
return;
|
|
5632
3314
|
}
|
|
5633
|
-
|
|
3315
|
+
const timeout = {
|
|
3316
|
+
kind: "terminal",
|
|
3317
|
+
idleMs,
|
|
3318
|
+
timeoutMs: turnTerminalIdleTimeoutMs,
|
|
3319
|
+
lastActivityReason: completionLastActivityReason,
|
|
3320
|
+
details: completionLastActivityDetails
|
|
3321
|
+
};
|
|
3322
|
+
params.onTimeout(timeout);
|
|
3323
|
+
params.onMarkTimedOut();
|
|
3324
|
+
params.onRecordEvent("turn.terminal_idle_timeout", {
|
|
3325
|
+
threadId: params.threadId,
|
|
3326
|
+
turnId: params.getTurnId(),
|
|
3327
|
+
idleMs,
|
|
3328
|
+
timeoutMs: timeout.timeoutMs,
|
|
3329
|
+
lastActivityReason: timeout.lastActivityReason,
|
|
3330
|
+
...timeout.details
|
|
3331
|
+
});
|
|
3332
|
+
embeddedAgentLog.warn("codex app-server turn idle timed out waiting for terminal event", {
|
|
3333
|
+
threadId: params.threadId,
|
|
3334
|
+
turnId: params.getTurnId(),
|
|
3335
|
+
idleMs,
|
|
3336
|
+
timeoutMs: timeout.timeoutMs,
|
|
3337
|
+
lastActivityReason: timeout.lastActivityReason,
|
|
3338
|
+
...timeout.details
|
|
3339
|
+
});
|
|
3340
|
+
params.onAbort("turn_terminal_idle_timeout");
|
|
5634
3341
|
}
|
|
5635
|
-
return
|
|
5636
|
-
|
|
5637
|
-
|
|
5638
|
-
|
|
5639
|
-
|
|
5640
|
-
|
|
5641
|
-
|
|
5642
|
-
|
|
5643
|
-
|
|
5644
|
-
|
|
5645
|
-
|
|
5646
|
-
|
|
5647
|
-
|
|
5648
|
-
|
|
5649
|
-
|
|
3342
|
+
return {
|
|
3343
|
+
isCompletionIdleWatchArmed: () => completionIdleWatchArmed,
|
|
3344
|
+
isCompletionIdleWatchPinnedByTerminalError: () => completionIdleWatchPinnedByTerminalError,
|
|
3345
|
+
isAssistantCompletionIdleWatchArmed: () => assistantCompletionIdleWatchArmed,
|
|
3346
|
+
armAttemptIdleWatch: () => {
|
|
3347
|
+
attemptIdleWatchArmed = true;
|
|
3348
|
+
scheduleAttemptIdleWatch();
|
|
3349
|
+
},
|
|
3350
|
+
armTerminalIdleWatch: () => {
|
|
3351
|
+
terminalIdleWatchArmed = true;
|
|
3352
|
+
scheduleTerminalIdleWatch();
|
|
3353
|
+
},
|
|
3354
|
+
armCompletionIdleWatch: (options) => {
|
|
3355
|
+
completionIdleWatchArmed = true;
|
|
3356
|
+
completionIdleWatchPinnedByTerminalError = options?.pinnedByTerminalError === true;
|
|
3357
|
+
completionIdleTimeoutOverrideMs = options?.timeoutMs !== void 0 ? resolveWatchTimeoutMs(options.timeoutMs) : void 0;
|
|
3358
|
+
scheduleCompletionIdleWatch();
|
|
3359
|
+
},
|
|
3360
|
+
disarmCompletionIdleWatch: () => {
|
|
3361
|
+
completionIdleWatchArmed = false;
|
|
3362
|
+
completionIdleWatchPinnedByTerminalError = false;
|
|
3363
|
+
completionIdleTimeoutOverrideMs = void 0;
|
|
3364
|
+
clearCompletionIdleTimer();
|
|
3365
|
+
},
|
|
3366
|
+
armAssistantCompletionIdleWatch: (details) => {
|
|
3367
|
+
assistantCompletionIdleWatchArmed = true;
|
|
3368
|
+
assistantCompletionLastActivityAt = Date.now();
|
|
3369
|
+
assistantCompletionLastActivityDetails = details;
|
|
3370
|
+
scheduleAssistantCompletionIdleWatch();
|
|
3371
|
+
},
|
|
3372
|
+
disarmAssistantCompletionIdleWatch: () => {
|
|
3373
|
+
assistantCompletionIdleWatchArmed = false;
|
|
3374
|
+
assistantCompletionLastActivityDetails = void 0;
|
|
3375
|
+
clearAssistantCompletionIdleTimer();
|
|
3376
|
+
},
|
|
3377
|
+
touchActivity: (reason, options) => {
|
|
3378
|
+
completionLastActivityAt = Date.now();
|
|
3379
|
+
completionLastActivityReason = reason;
|
|
3380
|
+
completionLastActivityDetails = options?.details;
|
|
3381
|
+
completionIdleTimeoutOverrideMs = void 0;
|
|
3382
|
+
if (options?.attemptProgress) recordAttemptProgress(reason, options);
|
|
3383
|
+
params.onProgressDiagnostic(reason);
|
|
3384
|
+
if (options?.arm) {
|
|
3385
|
+
completionIdleWatchArmed = true;
|
|
3386
|
+
completionIdleWatchPinnedByTerminalError = false;
|
|
3387
|
+
}
|
|
3388
|
+
scheduleProgressWatches();
|
|
3389
|
+
},
|
|
3390
|
+
noteNotificationReceived: (method, options) => {
|
|
3391
|
+
const now = Date.now();
|
|
3392
|
+
completionLastActivityAt = Math.max(completionLastActivityAt, Math.min(now, options?.receivedAtMs ?? now));
|
|
3393
|
+
completionLastActivityReason = `notification:${method}`;
|
|
3394
|
+
if (options?.details !== void 0) completionLastActivityDetails = options.details;
|
|
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
|
+
};
|
|
5650
3408
|
}
|
|
5651
3409
|
//#endregion
|
|
5652
3410
|
//#region extensions/codex/src/app-server/native-subagent-notification.ts
|
|
@@ -5792,8 +3550,7 @@ var CodexNativeSubagentTaskMirror = class {
|
|
|
5792
3550
|
constructor(params, runtime) {
|
|
5793
3551
|
this.params = params;
|
|
5794
3552
|
this.runtime = runtime;
|
|
5795
|
-
this.
|
|
5796
|
-
this.failedMirrorThreadIds = /* @__PURE__ */ new Set();
|
|
3553
|
+
this.mirrorStateByThreadId = /* @__PURE__ */ new Map();
|
|
5797
3554
|
this.terminalRunIds = /* @__PURE__ */ new Set();
|
|
5798
3555
|
this.authoritativeRunIds = /* @__PURE__ */ new Set();
|
|
5799
3556
|
this.expectedAuthoritativeRunIds = /* @__PURE__ */ new Set();
|
|
@@ -5827,8 +3584,8 @@ var CodexNativeSubagentTaskMirror = class {
|
|
|
5827
3584
|
const spawn = readSubagentThreadSpawnSource(thread.source, this.params.parentThreadId);
|
|
5828
3585
|
if (!spawn) return;
|
|
5829
3586
|
const threadId = thread.id.trim();
|
|
5830
|
-
if (!threadId || this.
|
|
5831
|
-
this.
|
|
3587
|
+
if (!threadId || this.mirrorStateByThreadId.get(threadId) === "mirrored") return;
|
|
3588
|
+
this.mirrorStateByThreadId.set(threadId, "mirrored");
|
|
5832
3589
|
const runId = codexNativeSubagentRunId(threadId);
|
|
5833
3590
|
const label = trimOptional(spawn.agent_nickname) ?? trimOptional(thread.agentNickname) ?? trimOptional(spawn.agent_role) ?? trimOptional(thread.agentRole) ?? "Codex subagent";
|
|
5834
3591
|
const task = trimOptional(thread.preview) ?? `Codex native subagent${label === "Codex subagent" ? "" : ` ${label}`}`;
|
|
@@ -5846,11 +3603,9 @@ var CodexNativeSubagentTaskMirror = class {
|
|
|
5846
3603
|
lastEventAt: this.now(),
|
|
5847
3604
|
progressSummary: "Codex native subagent started."
|
|
5848
3605
|
})) {
|
|
5849
|
-
this.
|
|
5850
|
-
this.failedMirrorThreadIds.add(threadId);
|
|
3606
|
+
this.mirrorStateByThreadId.set(threadId, "failed");
|
|
5851
3607
|
return;
|
|
5852
3608
|
}
|
|
5853
|
-
this.failedMirrorThreadIds.delete(threadId);
|
|
5854
3609
|
this.terminalRunIds.delete(runId);
|
|
5855
3610
|
this.authoritativeRunIds.delete(runId);
|
|
5856
3611
|
this.applyStatus(threadId, thread.status);
|
|
@@ -5861,7 +3616,7 @@ var CodexNativeSubagentTaskMirror = class {
|
|
|
5861
3616
|
this.applyStatus(notification.threadId, notification.status);
|
|
5862
3617
|
}
|
|
5863
3618
|
applyStatus(threadId, status) {
|
|
5864
|
-
if (
|
|
3619
|
+
if (this.mirrorStateByThreadId.get(threadId) === "failed") return;
|
|
5865
3620
|
const statusType = status?.type;
|
|
5866
3621
|
if (!statusType) return;
|
|
5867
3622
|
const runId = codexNativeSubagentRunId(threadId);
|
|
@@ -5911,7 +3666,7 @@ var CodexNativeSubagentTaskMirror = class {
|
|
|
5911
3666
|
const isSpawnAgentTool = normalizeToolName$1(readString$3(item, "tool")) === "spawnagent";
|
|
5912
3667
|
const receiverThreadIds = readStringArray$1(item.receiverThreadIds);
|
|
5913
3668
|
const agentsStates = readAgentsStates(item.agentsStates);
|
|
5914
|
-
const spawnChildThreadIds = new Set([...receiverThreadIds, ...agentsStates.keys()]);
|
|
3669
|
+
const spawnChildThreadIds = /* @__PURE__ */ new Set([...receiverThreadIds, ...agentsStates.keys()]);
|
|
5915
3670
|
if (isSpawnAgentTool) for (const childThreadId of spawnChildThreadIds) this.createTaskFromCollabSpawnItem(childThreadId, item);
|
|
5916
3671
|
const toolCallStatus = normalizeCollabToolCallStatus(readString$3(item, "status"));
|
|
5917
3672
|
const terminalToolCallThreadIds = /* @__PURE__ */ new Set();
|
|
@@ -5934,8 +3689,8 @@ var CodexNativeSubagentTaskMirror = class {
|
|
|
5934
3689
|
}
|
|
5935
3690
|
createTaskFromCollabSpawnItem(threadId, item) {
|
|
5936
3691
|
const normalizedThreadId = threadId.trim();
|
|
5937
|
-
if (!normalizedThreadId || this.
|
|
5938
|
-
this.
|
|
3692
|
+
if (!normalizedThreadId || this.mirrorStateByThreadId.get(normalizedThreadId) === "mirrored") return;
|
|
3693
|
+
this.mirrorStateByThreadId.set(normalizedThreadId, "mirrored");
|
|
5939
3694
|
const prompt = trimOptional(readString$3(item, "prompt"));
|
|
5940
3695
|
const runId = codexNativeSubagentRunId(normalizedThreadId);
|
|
5941
3696
|
const createdAt = this.now();
|
|
@@ -5952,16 +3707,14 @@ var CodexNativeSubagentTaskMirror = class {
|
|
|
5952
3707
|
lastEventAt: createdAt,
|
|
5953
3708
|
progressSummary: "Codex native subagent spawned."
|
|
5954
3709
|
})) {
|
|
5955
|
-
this.
|
|
5956
|
-
this.failedMirrorThreadIds.add(normalizedThreadId);
|
|
3710
|
+
this.mirrorStateByThreadId.set(normalizedThreadId, "failed");
|
|
5957
3711
|
return;
|
|
5958
3712
|
}
|
|
5959
|
-
this.failedMirrorThreadIds.delete(normalizedThreadId);
|
|
5960
3713
|
this.terminalRunIds.delete(runId);
|
|
5961
3714
|
this.authoritativeRunIds.delete(runId);
|
|
5962
3715
|
}
|
|
5963
3716
|
applyCollabAgentStatus(threadId, status, message) {
|
|
5964
|
-
if (
|
|
3717
|
+
if (this.mirrorStateByThreadId.get(threadId) === "failed") return;
|
|
5965
3718
|
const normalizedStatus = normalizeAgentStateStatus(status);
|
|
5966
3719
|
if (!normalizedStatus) return;
|
|
5967
3720
|
const runId = codexNativeSubagentRunId(threadId);
|
|
@@ -6159,6 +3912,7 @@ function registerCodexNativeSubagentMonitor(params) {
|
|
|
6159
3912
|
taskRuntimeScope: params.taskRuntimeScope,
|
|
6160
3913
|
agentId: params.agentId
|
|
6161
3914
|
});
|
|
3915
|
+
return monitor;
|
|
6162
3916
|
}
|
|
6163
3917
|
/** Tracks native subagent thread notifications, transcript completions, and task delivery. */
|
|
6164
3918
|
var CodexNativeSubagentMonitor = class {
|
|
@@ -6185,6 +3939,13 @@ var CodexNativeSubagentMonitor = class {
|
|
|
6185
3939
|
this.childThreadIdsByAgentPath.clear();
|
|
6186
3940
|
this.transcriptPathsByChildThreadId.clear();
|
|
6187
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
|
+
}
|
|
6188
3949
|
configure(options) {
|
|
6189
3950
|
const codexHome = normalizeOptionalString(options.codexHome);
|
|
6190
3951
|
if (codexHome) this.codexHome = codexHome;
|
|
@@ -6222,10 +3983,29 @@ var CodexNativeSubagentMonitor = class {
|
|
|
6222
3983
|
error: formatErrorMessage(error)
|
|
6223
3984
|
});
|
|
6224
3985
|
}
|
|
3986
|
+
this.markChildTurnStarted(notification);
|
|
3987
|
+
await this.handleChildSystemError(notification);
|
|
6225
3988
|
this.captureChildAssistantMessage(notification);
|
|
6226
3989
|
await this.handleChildTurnCompletion(notification);
|
|
6227
3990
|
await this.handleCompletionNotification(notification);
|
|
6228
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
|
+
}
|
|
6229
4009
|
ensureParentTaskRuntime(state) {
|
|
6230
4010
|
if (state.taskRuntime || !state.requesterSessionKey || !state.taskRuntimeScope) return;
|
|
6231
4011
|
state.taskRuntime = this.runtime.createAgentHarnessTaskRuntime({
|
|
@@ -6262,7 +4042,7 @@ var CodexNativeSubagentMonitor = class {
|
|
|
6262
4042
|
const parentThreadId = item ? (readString$2(item, "senderThreadId") ?? readString$2(params, "threadId"))?.trim() : void 0;
|
|
6263
4043
|
const state = parentThreadId ? this.parentStates.get(parentThreadId) : void 0;
|
|
6264
4044
|
if (state && parentThreadId) {
|
|
6265
|
-
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));
|
|
6266
4046
|
for (const childThreadId of childThreadIds) this.registerChildThread(parentThreadId, childThreadId);
|
|
6267
4047
|
}
|
|
6268
4048
|
return state;
|
|
@@ -6348,6 +4128,8 @@ var CodexNativeSubagentMonitor = class {
|
|
|
6348
4128
|
if (childState && turn && readString$2(turn, "status") === "interrupted") {
|
|
6349
4129
|
const turnId = readString$2(turn, "id");
|
|
6350
4130
|
if (turnId) childState.assistantMessagesByTurn.delete(turnId);
|
|
4131
|
+
childState.settledWithoutCompletion = true;
|
|
4132
|
+
await this.flushDeferredParentSettlements(childState.parentThreadId);
|
|
6351
4133
|
return;
|
|
6352
4134
|
}
|
|
6353
4135
|
if (childState && turn) this.captureChildTurnAssistantMessages(childState, turn);
|
|
@@ -6401,7 +4183,10 @@ var CodexNativeSubagentMonitor = class {
|
|
|
6401
4183
|
childState.noFinalCompletionFallbackTimer = void 0;
|
|
6402
4184
|
}
|
|
6403
4185
|
}
|
|
6404
|
-
if (!state.requesterSessionKey)
|
|
4186
|
+
if (!state.requesterSessionKey) {
|
|
4187
|
+
await this.flushDeferredParentSettlements(state.parentThreadId);
|
|
4188
|
+
return;
|
|
4189
|
+
}
|
|
6405
4190
|
const completionKey = buildCompletionDedupeKey(state.parentThreadId, completion);
|
|
6406
4191
|
if (state.deliveredCompletionKeys.has(completionKey)) return;
|
|
6407
4192
|
const deliveryState = childState ?? this.ensureChildState(state.parentThreadId, completion.childThreadId);
|
|
@@ -6454,6 +4239,7 @@ var CodexNativeSubagentMonitor = class {
|
|
|
6454
4239
|
});
|
|
6455
4240
|
} finally {
|
|
6456
4241
|
childState.deliveringCompletionKey = void 0;
|
|
4242
|
+
await this.flushDeferredParentSettlements(state.parentThreadId);
|
|
6457
4243
|
}
|
|
6458
4244
|
}
|
|
6459
4245
|
markCompletionDeliveryPending(completion, error) {
|
|
@@ -6486,6 +4272,28 @@ var CodexNativeSubagentMonitor = class {
|
|
|
6486
4272
|
}, delayMs);
|
|
6487
4273
|
unrefTimer(childState.completionDeliveryTimer);
|
|
6488
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
|
+
}
|
|
6489
4297
|
finalizeCompletionTask(completion, eventAt) {
|
|
6490
4298
|
const taskRuntime = this.getTaskRuntimeForChild(completion.childThreadId);
|
|
6491
4299
|
if (!taskRuntime) return;
|
|
@@ -6526,7 +4334,8 @@ var CodexNativeSubagentMonitor = class {
|
|
|
6526
4334
|
assistantMessagesByTurn: /* @__PURE__ */ new Map(),
|
|
6527
4335
|
transcriptPollAttempt: 0,
|
|
6528
4336
|
transcriptTerminal: false,
|
|
6529
|
-
completionDeliveryAttempt: 0
|
|
4337
|
+
completionDeliveryAttempt: 0,
|
|
4338
|
+
settledWithoutCompletion: false
|
|
6530
4339
|
};
|
|
6531
4340
|
this.childStates.set(normalizedChildThreadId, childState);
|
|
6532
4341
|
}
|
|
@@ -6965,7 +4774,8 @@ function parseCodexAppServerByteLimit(value) {
|
|
|
6965
4774
|
if (!match) return;
|
|
6966
4775
|
const amount = Number(match[1]);
|
|
6967
4776
|
if (!Number.isFinite(amount) || amount <= 0) return;
|
|
6968
|
-
const
|
|
4777
|
+
const unit = (match[2] ?? "b").toLowerCase();
|
|
4778
|
+
const multiplier = CODEX_APP_SERVER_BYTE_UNITS[unit];
|
|
6969
4779
|
if (multiplier === void 0) return;
|
|
6970
4780
|
return Math.max(1, Math.round(amount * multiplier));
|
|
6971
4781
|
}
|
|
@@ -7149,7 +4959,10 @@ async function rotateOversizedCodexAppServerStartupBinding(params) {
|
|
|
7149
4959
|
bytes: file.bytes
|
|
7150
4960
|
}))
|
|
7151
4961
|
});
|
|
7152
|
-
await
|
|
4962
|
+
await params.bindingStore.mutate(params.identity, {
|
|
4963
|
+
kind: "clear",
|
|
4964
|
+
threadId: binding.threadId
|
|
4965
|
+
});
|
|
7153
4966
|
return;
|
|
7154
4967
|
}
|
|
7155
4968
|
}
|
|
@@ -7178,7 +4991,10 @@ async function rotateOversizedCodexAppServerStartupBinding(params) {
|
|
|
7178
4991
|
reserveTokens,
|
|
7179
4992
|
projectedTurnTokens: params.projectedTurnTokens
|
|
7180
4993
|
});
|
|
7181
|
-
await
|
|
4994
|
+
await params.bindingStore.mutate(params.identity, {
|
|
4995
|
+
kind: "clear",
|
|
4996
|
+
threadId: binding.threadId
|
|
4997
|
+
});
|
|
7182
4998
|
return;
|
|
7183
4999
|
}
|
|
7184
5000
|
if (compaction?.truncateAfterCompaction !== true) return binding;
|
|
@@ -7462,7 +5278,7 @@ async function markCodexAuthProfileBlockedFromRateLimits(params) {
|
|
|
7462
5278
|
async function formatCodexTurnStartUsageLimitError(params) {
|
|
7463
5279
|
return refreshCodexUsageLimitError({
|
|
7464
5280
|
client: params.client,
|
|
7465
|
-
source: readCodexTurnStartUsageLimitErrorSource(params.error, params.
|
|
5281
|
+
source: readCodexTurnStartUsageLimitErrorSource(params.client, params.error, params.errorNotification, params.rateLimitsRevisionBeforeTurnStart),
|
|
7466
5282
|
timeoutMs: params.timeoutMs,
|
|
7467
5283
|
signal: params.signal
|
|
7468
5284
|
});
|
|
@@ -7475,7 +5291,7 @@ async function refreshCodexUsageLimitPromptError(params) {
|
|
|
7475
5291
|
source: {
|
|
7476
5292
|
message: params.message,
|
|
7477
5293
|
codexErrorInfo: "usageLimitExceeded",
|
|
7478
|
-
rateLimits: readRecentCodexRateLimits()
|
|
5294
|
+
rateLimits: readRecentCodexRateLimits(params.client)
|
|
7479
5295
|
},
|
|
7480
5296
|
timeoutMs: params.timeoutMs,
|
|
7481
5297
|
signal: params.signal
|
|
@@ -7513,7 +5329,7 @@ async function readCodexRateLimitsFromAppServerForUsageLimitError(params) {
|
|
|
7513
5329
|
timeoutMs: resolveCodexUsageLimitRateLimitRefreshTimeoutMs(params.timeoutMs),
|
|
7514
5330
|
signal: params.signal
|
|
7515
5331
|
});
|
|
7516
|
-
|
|
5332
|
+
rememberCodexRateLimitsRead(params.client, rateLimits);
|
|
7517
5333
|
return rateLimits;
|
|
7518
5334
|
} catch (error) {
|
|
7519
5335
|
embeddedAgentLog.debug("codex app-server rate-limit refresh failed after usage-limit error", { error: formatErrorMessage(error) });
|
|
@@ -7524,38 +5340,25 @@ function resolveCodexUsageLimitRateLimitRefreshTimeoutMs(timeoutMs) {
|
|
|
7524
5340
|
if (timeoutMs === void 0 || !Number.isFinite(timeoutMs) || timeoutMs <= 0) return CODEX_USAGE_LIMIT_RATE_LIMIT_REFRESH_TIMEOUT_MS;
|
|
7525
5341
|
return Math.max(100, Math.min(timeoutMs, CODEX_USAGE_LIMIT_RATE_LIMIT_REFRESH_TIMEOUT_MS));
|
|
7526
5342
|
}
|
|
7527
|
-
function readCodexTurnStartUsageLimitErrorSource(error,
|
|
7528
|
-
const notificationError =
|
|
7529
|
-
const notificationRateLimits = readLatestRateLimitNotificationPayload(pendingNotifications);
|
|
5343
|
+
function readCodexTurnStartUsageLimitErrorSource(client, error, errorNotification, rateLimitsRevisionBeforeTurnStart) {
|
|
5344
|
+
const notificationError = readCodexErrorNotification(errorNotification);
|
|
7530
5345
|
const errorPayload = readCodexErrorPayload(error);
|
|
7531
|
-
const rateLimits =
|
|
5346
|
+
const rateLimits = errorPayload.rateLimits ?? readRecentCodexRateLimits(client);
|
|
5347
|
+
const cacheUpdatedDuringTurnStart = rateLimitsRevisionBeforeTurnStart !== void 0 && readCodexRateLimitsRevision(client) > rateLimitsRevisionBeforeTurnStart;
|
|
7532
5348
|
return {
|
|
7533
5349
|
message: notificationError?.message ?? errorPayload.message ?? formatErrorMessage(error),
|
|
7534
5350
|
codexErrorInfo: notificationError?.codexErrorInfo ?? errorPayload.codexErrorInfo,
|
|
7535
5351
|
rateLimits,
|
|
7536
|
-
rateLimitsTrustedForProfile:
|
|
5352
|
+
rateLimitsTrustedForProfile: errorPayload.rateLimits !== void 0 || cacheUpdatedDuringTurnStart
|
|
7537
5353
|
};
|
|
7538
5354
|
}
|
|
7539
|
-
function
|
|
7540
|
-
|
|
7541
|
-
|
|
7542
|
-
|
|
7543
|
-
|
|
7544
|
-
|
|
7545
|
-
|
|
7546
|
-
}
|
|
7547
|
-
}
|
|
7548
|
-
function readLatestCodexErrorNotification(notifications) {
|
|
7549
|
-
for (let index = notifications.length - 1; index >= 0; index -= 1) {
|
|
7550
|
-
const notification = notifications[index];
|
|
7551
|
-
if (notification?.method !== "error" || !isJsonObject(notification.params)) continue;
|
|
7552
|
-
const error = notification.params.error;
|
|
7553
|
-
if (!isJsonObject(error)) continue;
|
|
7554
|
-
return {
|
|
7555
|
-
message: readString$1(error, "message"),
|
|
7556
|
-
codexErrorInfo: error.codexErrorInfo
|
|
7557
|
-
};
|
|
7558
|
-
}
|
|
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;
|
|
7559
5362
|
}
|
|
7560
5363
|
function readCodexErrorPayload(error) {
|
|
7561
5364
|
const message = error instanceof Error ? error.message : void 0;
|
|
@@ -7564,7 +5367,6 @@ function readCodexErrorPayload(error) {
|
|
|
7564
5367
|
if (!isJsonObject(data)) return { message };
|
|
7565
5368
|
const nestedError = isJsonObject(data.error) ? data.error : data;
|
|
7566
5369
|
const rateLimits = nestedError.rateLimits ?? data.rateLimits;
|
|
7567
|
-
if (rateLimits !== void 0) rememberCodexRateLimits(rateLimits);
|
|
7568
5370
|
return {
|
|
7569
5371
|
message: readString$1(nestedError, "message") ?? message,
|
|
7570
5372
|
codexErrorInfo: nestedError.codexErrorInfo,
|
|
@@ -7704,7 +5506,6 @@ function readRequestId(record) {
|
|
|
7704
5506
|
//#region extensions/codex/src/app-server/run-attempt.ts
|
|
7705
5507
|
const CODEX_NATIVE_HOOK_RELAY_RENEW_INTERVAL_MS = 6e4;
|
|
7706
5508
|
const CODEX_APP_SERVER_PROJECTED_CHARS_PER_TOKEN = 4;
|
|
7707
|
-
const CODEX_APP_SERVER_ACTIVE_NATIVE_TURN_WAIT_TIMEOUT_MS = 3e4;
|
|
7708
5509
|
function shouldKeepCodexSharedAbortOpen(params) {
|
|
7709
5510
|
if (params.explicitCancellationObserved || params.result.aborted || params.result.externalAbort) return false;
|
|
7710
5511
|
return params.trigger === "memory" || !params.attemptSucceeded;
|
|
@@ -7792,7 +5593,7 @@ function toTranscriptToolResultContentItem(item) {
|
|
|
7792
5593
|
}
|
|
7793
5594
|
function formatUnsupportedCodexDynamicToolOutput(type) {
|
|
7794
5595
|
const rawType = typeof type === "string" ? type.replace(/\s+/g, " ").trim() : "";
|
|
7795
|
-
return `[Unsupported Codex dynamic tool output: ${rawType ? rawType
|
|
5596
|
+
return `[Unsupported Codex dynamic tool output: ${rawType ? truncateUtf16Safe(rawType, 80) : "unknown"}${rawType.length > 80 ? "..." : ""}]`;
|
|
7796
5597
|
}
|
|
7797
5598
|
function shouldAwaitCodexAgentEndHook(params) {
|
|
7798
5599
|
return !params.messageChannel && !params.messageProvider;
|
|
@@ -7811,7 +5612,7 @@ async function runCodexAgentEndHook(params, hookParams) {
|
|
|
7811
5612
|
}
|
|
7812
5613
|
runAgentEndSideEffects(sideEffectParams);
|
|
7813
5614
|
}
|
|
7814
|
-
async function runCodexAppServerAttempt(params, options
|
|
5615
|
+
async function runCodexAppServerAttempt(params, options) {
|
|
7815
5616
|
const attemptStartedAt = Date.now();
|
|
7816
5617
|
const profilerEnabled = isCodexAppServerProfilerEnabled(params.config);
|
|
7817
5618
|
const codexModelCallTrace = freezeDiagnosticTraceContext(createDiagnosticTraceContextFromActiveScope());
|
|
@@ -7823,7 +5624,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
7823
5624
|
resetAnnounced: false
|
|
7824
5625
|
};
|
|
7825
5626
|
const preDynamicStartupStages = createCodexDynamicToolBuildStageTracker({ enabled: profilerEnabled });
|
|
7826
|
-
const attemptClientFactory = options.clientFactory ??
|
|
5627
|
+
const attemptClientFactory = options.clientFactory ?? getLeasedSharedCodexAppServerClient;
|
|
7827
5628
|
const pluginConfig = readCodexPluginConfig(options.pluginConfig);
|
|
7828
5629
|
const computerUseConfig = resolveCodexComputerUseConfig({ pluginConfig });
|
|
7829
5630
|
const { sessionAgentId } = resolveSessionAgentIds$1({
|
|
@@ -7851,10 +5652,17 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
7851
5652
|
agentId: sessionAgentId
|
|
7852
5653
|
});
|
|
7853
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;
|
|
7854
5662
|
preDynamicStartupStages.mark("session-agent");
|
|
7855
5663
|
const activeContextEngine = isActiveHarnessContextEngine(params.contextEngine) ? params.contextEngine : void 0;
|
|
7856
5664
|
const isInactiveThreadBootstrapBinding = (binding) => !activeContextEngine && binding?.contextEngine?.projection?.mode === "thread_bootstrap";
|
|
7857
|
-
let startupBinding = await
|
|
5665
|
+
let startupBinding = await bindingStore.read(bindingIdentity);
|
|
7858
5666
|
preDynamicStartupStages.mark("read-binding");
|
|
7859
5667
|
const startupBindingAuthProfileId = startupBinding?.authProfileId;
|
|
7860
5668
|
const initialStartupBindingHadInactiveThreadBootstrap = isInactiveThreadBootstrapBinding(startupBinding);
|
|
@@ -7953,6 +5761,8 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
7953
5761
|
else params.abortSignal?.addEventListener("abort", abortFromUpstream, { once: true });
|
|
7954
5762
|
startupBinding = await rotateOversizedCodexAppServerStartupBinding({
|
|
7955
5763
|
binding: startupBinding,
|
|
5764
|
+
bindingStore,
|
|
5765
|
+
identity: bindingIdentity,
|
|
7956
5766
|
sessionFile: params.sessionFile,
|
|
7957
5767
|
agentDir,
|
|
7958
5768
|
codexHome: appServer.start.env?.CODEX_HOME,
|
|
@@ -8158,6 +5968,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
8158
5968
|
hookContext: {
|
|
8159
5969
|
agentId: sessionAgentId,
|
|
8160
5970
|
config: params.config,
|
|
5971
|
+
workspaceDir: effectiveWorkspace,
|
|
8161
5972
|
sessionId: params.sessionId,
|
|
8162
5973
|
sessionKey: sandboxSessionKey,
|
|
8163
5974
|
runId: params.runId,
|
|
@@ -8406,8 +6217,8 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
8406
6217
|
codexTurnPromptText = decorateCodexTurnPromptText(nextPromptBuild);
|
|
8407
6218
|
};
|
|
8408
6219
|
const selectNewerVisibleHistoryAfterBinding = (binding) => {
|
|
8409
|
-
const
|
|
8410
|
-
|
|
6220
|
+
const historyCoveredThrough = Date.parse(binding.historyCoveredThrough ?? "");
|
|
6221
|
+
const cutoff = Number.isFinite(historyCoveredThrough) ? historyCoveredThrough : 0;
|
|
8411
6222
|
return historyMessages.filter((message) => {
|
|
8412
6223
|
if (message.role !== "user" && message.role !== "assistant") return false;
|
|
8413
6224
|
const record = message;
|
|
@@ -8415,9 +6226,10 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
8415
6226
|
if (typeof idempotencyKey === "string" && idempotencyKey.startsWith("codex-app-server:")) return false;
|
|
8416
6227
|
const meta = record["__openclaw"];
|
|
8417
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;
|
|
8418
6230
|
if (typeof mirrorIdentity === "string" && mirrorIdentity.startsWith("codex-app-server:")) return false;
|
|
8419
6231
|
const timestamp = typeof message.timestamp === "number" ? message.timestamp : typeof message.timestamp === "string" ? Date.parse(message.timestamp) : NaN;
|
|
8420
|
-
return Number.isFinite(timestamp) && timestamp >
|
|
6232
|
+
return Number.isFinite(timestamp) && timestamp > cutoff;
|
|
8421
6233
|
});
|
|
8422
6234
|
};
|
|
8423
6235
|
const applyResumeStaleBindingContinuityProjection = (binding) => {
|
|
@@ -8469,6 +6281,8 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
8469
6281
|
});
|
|
8470
6282
|
startupBinding = await rotateOversizedCodexAppServerStartupBinding({
|
|
8471
6283
|
binding: startupBinding,
|
|
6284
|
+
bindingStore,
|
|
6285
|
+
identity: bindingIdentity,
|
|
8472
6286
|
sessionFile: params.sessionFile,
|
|
8473
6287
|
agentDir,
|
|
8474
6288
|
codexHome: appServer.start.env?.CODEX_HOME,
|
|
@@ -8516,11 +6330,41 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
8516
6330
|
});
|
|
8517
6331
|
let client;
|
|
8518
6332
|
let thread;
|
|
6333
|
+
let turnRouter;
|
|
6334
|
+
let turnRoute;
|
|
6335
|
+
let routeActivated = false;
|
|
6336
|
+
let detachRouteAbort = () => void 0;
|
|
8519
6337
|
let trajectoryEndRecorded = false;
|
|
8520
6338
|
const markTrajectoryEndRecorded = () => {
|
|
8521
6339
|
trajectoryEndRecorded = true;
|
|
8522
6340
|
};
|
|
8523
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
|
+
};
|
|
8524
6368
|
let releaseSharedClientLease;
|
|
8525
6369
|
let sharedCodexClientRetiredForOneShotCleanup = false;
|
|
8526
6370
|
const releaseSharedClientLeaseOnce = () => {
|
|
@@ -8558,6 +6402,13 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
8558
6402
|
await releaseCodexSandboxExecServerEnvironment(sandbox);
|
|
8559
6403
|
}
|
|
8560
6404
|
};
|
|
6405
|
+
const releaseCurrentRoute = () => {
|
|
6406
|
+
detachRouteAbort();
|
|
6407
|
+
detachRouteAbort = () => void 0;
|
|
6408
|
+
turnRoute?.release();
|
|
6409
|
+
turnRoute = void 0;
|
|
6410
|
+
routeActivated = false;
|
|
6411
|
+
};
|
|
8561
6412
|
let codexEnvironmentSelection;
|
|
8562
6413
|
let codexExecutionCwd = effectiveCwd;
|
|
8563
6414
|
let codexSandboxPolicy;
|
|
@@ -8582,7 +6433,13 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
8582
6433
|
attemptTimeoutMs: params.timeoutMs,
|
|
8583
6434
|
startupTimeoutMs,
|
|
8584
6435
|
turnStartTimeoutMs: params.timeoutMs,
|
|
8585
|
-
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
|
+
}
|
|
8586
6443
|
});
|
|
8587
6444
|
return {
|
|
8588
6445
|
configPatch: nativeHookRelay ? buildCodexNativeHookRelayConfig({
|
|
@@ -8602,6 +6459,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
8602
6459
|
pluginAppServer = attemptAppServer;
|
|
8603
6460
|
const startupResult = await startCodexAttemptThread({
|
|
8604
6461
|
attemptClientFactory,
|
|
6462
|
+
bindingStore,
|
|
8605
6463
|
appServer: attemptAppServer,
|
|
8606
6464
|
pluginConfig,
|
|
8607
6465
|
computerUseConfig,
|
|
@@ -8634,6 +6492,8 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
8634
6492
|
});
|
|
8635
6493
|
client = startupResult.client;
|
|
8636
6494
|
thread = startupResult.thread;
|
|
6495
|
+
turnRouter = startupResult.turnRouter;
|
|
6496
|
+
turnRoute = startupResult.turnRoute;
|
|
8637
6497
|
pluginAppServer = startupResult.pluginAppServer;
|
|
8638
6498
|
if (thread.lifecycle.action === "started") {
|
|
8639
6499
|
const activeThreadReviewerPolicyContext = resolveCodexModelBackedReviewerPolicyContext({
|
|
@@ -8689,6 +6549,8 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
8689
6549
|
}
|
|
8690
6550
|
});
|
|
8691
6551
|
} catch (error) {
|
|
6552
|
+
activateNativePreToolUseFailureFallback();
|
|
6553
|
+
releaseCurrentRoute();
|
|
8692
6554
|
nativeHookRelay?.unregister();
|
|
8693
6555
|
await releaseSandboxExecEnvironment();
|
|
8694
6556
|
params.abortSignal?.removeEventListener("abort", abortFromUpstream);
|
|
@@ -8709,7 +6571,8 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
8709
6571
|
prompt: codexTurnPromptText,
|
|
8710
6572
|
tools: toolBridge.availableSpecs
|
|
8711
6573
|
});
|
|
8712
|
-
|
|
6574
|
+
let latestStartupErrorNotification;
|
|
6575
|
+
let rateLimitsRevisionBeforeLastTurnStart;
|
|
8713
6576
|
let completed = false;
|
|
8714
6577
|
let terminalTurnNotificationQueued = false;
|
|
8715
6578
|
let timedOut = false;
|
|
@@ -8729,7 +6592,6 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
8729
6592
|
const completion = new Promise((resolve) => {
|
|
8730
6593
|
resolveCompletion = resolve;
|
|
8731
6594
|
});
|
|
8732
|
-
let notificationQueue = Promise.resolve();
|
|
8733
6595
|
const turnCompletionIdleTimeoutMs = resolveCodexTurnCompletionIdleTimeoutMs(options.turnCompletionIdleTimeoutMs ?? appServer.turnCompletionIdleTimeoutMs);
|
|
8734
6596
|
const turnAssistantCompletionIdleTimeoutMs = resolveCodexTurnAssistantCompletionIdleTimeoutMs(options.turnAssistantCompletionIdleTimeoutMs);
|
|
8735
6597
|
const postToolRawAssistantCompletionIdleTimeoutMs = resolveCodexPostToolRawAssistantCompletionIdleTimeoutMs(options.postToolRawAssistantCompletionIdleTimeoutMs ?? appServer.postToolRawAssistantCompletionIdleTimeoutMs, turnAssistantCompletionIdleTimeoutMs);
|
|
@@ -8749,7 +6611,6 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
8749
6611
|
let terminalDynamicToolReleaseCheckScheduled = false;
|
|
8750
6612
|
let currentTurnHadNonTerminalDynamicToolResult = false;
|
|
8751
6613
|
const turnIdRef = {};
|
|
8752
|
-
const projectorRef = {};
|
|
8753
6614
|
const userInputBridgeRef = {};
|
|
8754
6615
|
const steeringQueueRef = {};
|
|
8755
6616
|
const renewNativeHookRelayForTurnProgress = () => {
|
|
@@ -8901,6 +6762,21 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
8901
6762
|
});
|
|
8902
6763
|
lifecycleTerminalEmitted = true;
|
|
8903
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
|
+
};
|
|
8904
6780
|
const executionPhaseKeys = /* @__PURE__ */ new Set();
|
|
8905
6781
|
const emitExecutionPhaseOnce = (key, info) => {
|
|
8906
6782
|
if (executionPhaseKeys.has(key)) return;
|
|
@@ -8978,7 +6854,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
8978
6854
|
const steeringQueue = steeringQueueRef.current;
|
|
8979
6855
|
userInputBridge?.handleNotification(notification);
|
|
8980
6856
|
if (!projector || !turnId) {
|
|
8981
|
-
|
|
6857
|
+
if (notification.method === "error") latestStartupErrorNotification = notification;
|
|
8982
6858
|
return;
|
|
8983
6859
|
}
|
|
8984
6860
|
const notificationState = applyCodexTurnNotificationState({
|
|
@@ -9050,85 +6926,43 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
9050
6926
|
}
|
|
9051
6927
|
}
|
|
9052
6928
|
};
|
|
9053
|
-
|
|
9054
|
-
|
|
9055
|
-
|
|
9056
|
-
|
|
9057
|
-
|
|
9058
|
-
|
|
9059
|
-
let settled = false;
|
|
9060
|
-
const timeoutRef = {};
|
|
9061
|
-
const finish = (completedNativeTurn) => {
|
|
9062
|
-
if (settled) return;
|
|
9063
|
-
settled = true;
|
|
9064
|
-
if (timeoutRef.current) clearTimeout(timeoutRef.current);
|
|
9065
|
-
runAbortController.signal.removeEventListener("abort", abortListener);
|
|
9066
|
-
if (activeNativeTurnCompletionWaiter?.resolve === finishComplete) activeNativeTurnCompletionWaiter = void 0;
|
|
9067
|
-
resolve(completedNativeTurn);
|
|
9068
|
-
};
|
|
9069
|
-
const finishComplete = () => finish(true);
|
|
9070
|
-
const abortListener = () => finish(false);
|
|
9071
|
-
timeoutRef.current = setTimeout(() => finish(false), Math.min(appServer.requestTimeoutMs, CODEX_APP_SERVER_ACTIVE_NATIVE_TURN_WAIT_TIMEOUT_MS));
|
|
9072
|
-
activeNativeTurnCompletionWaiter = {
|
|
9073
|
-
matches: matchesCompletion,
|
|
9074
|
-
resolve: finishComplete
|
|
9075
|
-
};
|
|
9076
|
-
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
|
|
9077
6935
|
});
|
|
9078
6936
|
};
|
|
9079
|
-
const
|
|
6937
|
+
const noteNotificationReceived = (notification, scope, receivedAtMs) => {
|
|
9080
6938
|
const projector = projectorRef.current;
|
|
9081
6939
|
const turnId = turnIdRef.current;
|
|
9082
|
-
|
|
9083
|
-
const correlation = describeCodexNotificationCorrelation(notification, {
|
|
9084
|
-
threadId: thread.threadId,
|
|
9085
|
-
...turnId ? { turnId } : {}
|
|
9086
|
-
});
|
|
9087
|
-
embeddedAgentLog.trace("codex app-server raw notification received", correlation);
|
|
9088
|
-
if (notification.method === "turn/completed" && correlation.matchesActiveTurn === false) if (correlation.matchesActiveThread) embeddedAgentLog.warn("codex app-server turn/completed did not match active turn", correlation);
|
|
9089
|
-
else embeddedAgentLog.debug("codex app-server turn/completed ignored for other subscribed thread", correlation);
|
|
9090
|
-
if (notification.method === "turn/completed" && correlation.matchesActiveThread) {
|
|
9091
|
-
if (activeNativeTurnCompletionWaiter?.matches(notification)) activeNativeTurnCompletionWaiter.resolve();
|
|
9092
|
-
}
|
|
9093
|
-
if (isCodexNotificationOutsideActiveRun(correlation)) return Promise.resolve();
|
|
9094
|
-
if (!projector || !turnId) {
|
|
9095
|
-
userInputBridge?.handleNotification(notification);
|
|
9096
|
-
pendingNotifications.push(notification);
|
|
9097
|
-
return Promise.resolve();
|
|
9098
|
-
}
|
|
6940
|
+
if (!projector || !turnId) return;
|
|
9099
6941
|
if (isTerminalTurnNotificationForTurn(notification, turnId)) terminalTurnNotificationQueued = true;
|
|
9100
|
-
|
|
9101
|
-
const nativeResponseStreamDeltaMatchesActiveTurn = isNativeResponseStreamDelta && (correlation.matchesActiveTurn === true || isUnscopedCodexNotification(correlation) && canAttributeUnscopedNativeResponseDeltaToThisTurn(client));
|
|
9102
|
-
const notificationMatchesActiveTurn = correlation.matchesActiveTurn === true || !isNativeResponseStreamDelta && correlation.matchesActiveTurn !== false || nativeResponseStreamDeltaMatchesActiveTurn;
|
|
9103
|
-
if (correlation.matchesActiveTurn === true) {
|
|
6942
|
+
if (scope.turnId === turnId) {
|
|
9104
6943
|
const modelToolCallId = readRawResponseToolCallId(notification);
|
|
9105
6944
|
if (modelToolCallId) allocateCodexToolOutcomeOrdinal?.(modelToolCallId);
|
|
9106
6945
|
const nativeItem = readCodexNotificationItem(notification.params);
|
|
9107
6946
|
if (nativeItem?.type === "webSearch") projector.recordNativeToolOutcome(nativeItem);
|
|
9108
6947
|
}
|
|
9109
|
-
if (
|
|
9110
|
-
|
|
9111
|
-
|
|
9112
|
-
turnWatches.disarmAssistantCompletionIdleWatch();
|
|
9113
|
-
}
|
|
9114
|
-
turnWatches.noteNotificationReceived(notification.method, isNativeResponseStreamDelta ? {
|
|
9115
|
-
attemptProgress: true,
|
|
9116
|
-
...turnCrossedToolHandoff ? { attemptTimeoutMs: postToolRawAssistantCompletionIdleTimeoutMs } : {},
|
|
9117
|
-
details: { lastNotificationMethod: notification.method }
|
|
9118
|
-
} : void 0);
|
|
6948
|
+
if (readCodexFinalizationHookNotification(notification, thread.threadId, turnId)?.phase === "started") {
|
|
6949
|
+
unsettledFinalizationHookCount += 1;
|
|
6950
|
+
turnWatches.disarmAssistantCompletionIdleWatch();
|
|
9119
6951
|
}
|
|
9120
|
-
|
|
9121
|
-
|
|
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);
|
|
9122
6960
|
};
|
|
9123
6961
|
const drainNotificationQueue = async () => {
|
|
9124
|
-
|
|
9125
|
-
const queued = notificationQueue;
|
|
9126
|
-
await queued;
|
|
9127
|
-
if (queued === notificationQueue) return;
|
|
9128
|
-
}
|
|
6962
|
+
await turnRoute?.drain();
|
|
9129
6963
|
};
|
|
9130
6964
|
const nativeSubagentCodexHome = appServer.start.transport === "stdio" ? appServer.start.env?.CODEX_HOME ?? resolveCodexAppServerHomeDir(agentDir) : void 0;
|
|
9131
|
-
registerCodexNativeSubagentMonitor({
|
|
6965
|
+
nativeSubagentMonitorRef.current = registerCodexNativeSubagentMonitor({
|
|
9132
6966
|
client,
|
|
9133
6967
|
parentThreadId: thread.threadId,
|
|
9134
6968
|
requesterSessionKey: params.sessionKey,
|
|
@@ -9136,8 +6970,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
9136
6970
|
agentId: params.agentId,
|
|
9137
6971
|
codexHome: nativeSubagentCodexHome
|
|
9138
6972
|
});
|
|
9139
|
-
const
|
|
9140
|
-
const requestCleanup = client.addRequestHandler(async (request) => {
|
|
6973
|
+
const handleServerRequest = async (request, scope) => {
|
|
9141
6974
|
const turnId = turnIdRef.current;
|
|
9142
6975
|
const userInputBridge = userInputBridgeRef.current;
|
|
9143
6976
|
const projector = projectorRef.current;
|
|
@@ -9151,14 +6984,9 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
9151
6984
|
turnWatches.touchActivity(`request:${request.method}:start`, { attemptProgress: true });
|
|
9152
6985
|
};
|
|
9153
6986
|
try {
|
|
9154
|
-
if (request.method === "account/chatgptAuthTokens/refresh") return refreshCodexAppServerAuthTokens({
|
|
9155
|
-
agentDir,
|
|
9156
|
-
authProfileId: startupAuthProfileId,
|
|
9157
|
-
config: params.config
|
|
9158
|
-
});
|
|
9159
6987
|
if (!turnId) return;
|
|
9160
6988
|
if (request.method === "mcpServer/elicitation/request") {
|
|
9161
|
-
if (
|
|
6989
|
+
if (!scope.turnId || scope.turnId === turnId) {
|
|
9162
6990
|
armCompletionWatchOnResponse = true;
|
|
9163
6991
|
markCurrentTurnRequestProgress();
|
|
9164
6992
|
}
|
|
@@ -9173,7 +7001,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
9173
7001
|
});
|
|
9174
7002
|
}
|
|
9175
7003
|
if (request.method === "item/tool/requestUserInput") {
|
|
9176
|
-
if (
|
|
7004
|
+
if (scope.turnId === turnId) {
|
|
9177
7005
|
armCompletionWatchOnResponse = true;
|
|
9178
7006
|
markCurrentTurnRequestProgress();
|
|
9179
7007
|
}
|
|
@@ -9184,7 +7012,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
9184
7012
|
}
|
|
9185
7013
|
if (request.method !== "item/tool/call") {
|
|
9186
7014
|
if (isCodexAppServerApprovalRequest(request.method)) {
|
|
9187
|
-
if (
|
|
7015
|
+
if (scope.turnId === turnId) {
|
|
9188
7016
|
armCompletionWatchOnResponse = true;
|
|
9189
7017
|
markCurrentTurnRequestProgress();
|
|
9190
7018
|
}
|
|
@@ -9199,7 +7027,8 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
9199
7027
|
execReviewerAgentId: sessionAgentId,
|
|
9200
7028
|
internalExecAutoReview: appServer.approvalsReviewer === "user",
|
|
9201
7029
|
autoApprove: shouldAutoApproveCodexAppServerApprovals(appServer),
|
|
9202
|
-
signal: runAbortController.signal
|
|
7030
|
+
signal: runAbortController.signal,
|
|
7031
|
+
onNativeToolFailureDisposition: (itemId, disposition) => projector?.recordNativeToolApprovalFailure(itemId, disposition)
|
|
9203
7032
|
});
|
|
9204
7033
|
}
|
|
9205
7034
|
return;
|
|
@@ -9230,6 +7059,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
9230
7059
|
});
|
|
9231
7060
|
emitDynamicToolStartedDiagnostic({
|
|
9232
7061
|
call,
|
|
7062
|
+
agentId: sessionAgentId,
|
|
9233
7063
|
runId: params.runId,
|
|
9234
7064
|
sessionId: params.sessionId,
|
|
9235
7065
|
sessionKey: params.sessionKey
|
|
@@ -9337,6 +7167,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
9337
7167
|
})) emitDynamicToolTerminalDiagnostic({
|
|
9338
7168
|
response,
|
|
9339
7169
|
call,
|
|
7170
|
+
agentId: sessionAgentId,
|
|
9340
7171
|
runId: params.runId,
|
|
9341
7172
|
sessionId: params.sessionId,
|
|
9342
7173
|
sessionKey: params.sessionKey,
|
|
@@ -9363,6 +7194,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
9363
7194
|
sessionKey: params.sessionKey
|
|
9364
7195
|
})) emitDynamicToolErrorDiagnostic({
|
|
9365
7196
|
call,
|
|
7197
|
+
agentId: sessionAgentId,
|
|
9366
7198
|
runId: params.runId,
|
|
9367
7199
|
sessionId: params.sessionId,
|
|
9368
7200
|
sessionKey: params.sessionKey,
|
|
@@ -9386,7 +7218,63 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
9386
7218
|
scheduleTerminalDynamicToolReleaseCheck();
|
|
9387
7219
|
} else turnWatches.scheduleProgressWatches();
|
|
9388
7220
|
}
|
|
9389
|
-
}
|
|
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
|
+
}
|
|
9390
7278
|
const buildLlmInputEvent = () => ({
|
|
9391
7279
|
runId: params.runId,
|
|
9392
7280
|
sessionId: params.sessionId,
|
|
@@ -9433,6 +7321,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
9433
7321
|
throw error;
|
|
9434
7322
|
};
|
|
9435
7323
|
const startCodexTurn = async () => {
|
|
7324
|
+
const activeTurnRoute = await ensureCurrentThreadRoute();
|
|
9436
7325
|
const turnAppServer = withCodexAppServerFastModeServiceTier(pluginAppServer, params);
|
|
9437
7326
|
pluginAppServer = turnAppServer;
|
|
9438
7327
|
const turnStartParams = buildTurnStartParams(params, {
|
|
@@ -9450,31 +7339,42 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
9450
7339
|
heartbeatCollaborationInstructions: workspaceBootstrapContext.heartbeatCollaborationInstructions
|
|
9451
7340
|
});
|
|
9452
7341
|
codexModelCallDiagnostics.setRequestPayloadBytes(utf8JsonByteLength(turnStartParams));
|
|
9453
|
-
|
|
9454
|
-
|
|
9455
|
-
|
|
9456
|
-
|
|
9457
|
-
|
|
9458
|
-
|
|
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
|
+
}
|
|
9459
7365
|
};
|
|
9460
|
-
|
|
9461
|
-
|
|
9462
|
-
embeddedAgentLog.info("codex app-server resumed thread has active native turn; waiting before turn/start", {
|
|
9463
|
-
threadId: thread.threadId,
|
|
9464
|
-
activeTurnIds: activeNativeTurnIds
|
|
9465
|
-
});
|
|
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 });
|
|
9466
7368
|
emitCodexAppServerEvent(params, {
|
|
9467
7369
|
stream: "codex_app_server.lifecycle",
|
|
9468
7370
|
data: {
|
|
9469
7371
|
phase: "turn_start_waiting_for_native_turn",
|
|
9470
|
-
threadId: thread.threadId
|
|
9471
|
-
activeTurnIds: activeNativeTurnIds
|
|
7372
|
+
threadId: thread.threadId
|
|
9472
7373
|
}
|
|
9473
7374
|
});
|
|
9474
|
-
|
|
9475
|
-
|
|
9476
|
-
|
|
9477
|
-
});
|
|
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 });
|
|
9478
7378
|
}
|
|
9479
7379
|
try {
|
|
9480
7380
|
codexModelCallDiagnostics.emitStarted();
|
|
@@ -9520,25 +7420,25 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
9520
7420
|
error: formatErrorMessage(turnStartError)
|
|
9521
7421
|
});
|
|
9522
7422
|
try {
|
|
9523
|
-
|
|
9524
|
-
|
|
9525
|
-
|
|
9526
|
-
|
|
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", {
|
|
9527
7427
|
threadId: thread.threadId,
|
|
9528
7428
|
error: formatErrorMessage(turnStartError)
|
|
9529
7429
|
});
|
|
9530
7430
|
else {
|
|
9531
7431
|
thread = await restartContextEngineCodexThread();
|
|
9532
7432
|
{
|
|
9533
|
-
const retryBinding = await
|
|
7433
|
+
const retryBinding = await bindingStore.read(bindingIdentity);
|
|
9534
7434
|
if (retryBinding && retryBinding.threadId === thread.threadId && retryBinding.contextEngine?.projection) {
|
|
9535
|
-
|
|
9536
|
-
|
|
9537
|
-
|
|
9538
|
-
contextEngine:
|
|
9539
|
-
...
|
|
7435
|
+
await bindingStore.mutate(bindingIdentity, {
|
|
7436
|
+
kind: "patch",
|
|
7437
|
+
threadId: retryBinding.threadId,
|
|
7438
|
+
patch: { contextEngine: retryBinding.contextEngine ? {
|
|
7439
|
+
...retryBinding.contextEngine,
|
|
9540
7440
|
projection: void 0
|
|
9541
|
-
} : void 0
|
|
7441
|
+
} : void 0 }
|
|
9542
7442
|
});
|
|
9543
7443
|
embeddedAgentLog.info("codex app-server cleared stale context-engine projection after overflow retry", {
|
|
9544
7444
|
threadId: thread.threadId,
|
|
@@ -9567,12 +7467,13 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
9567
7467
|
const usageLimitError = await formatCodexTurnStartUsageLimitError({
|
|
9568
7468
|
client,
|
|
9569
7469
|
error: turnStartError,
|
|
9570
|
-
|
|
7470
|
+
errorNotification: latestStartupErrorNotification,
|
|
7471
|
+
rateLimitsRevisionBeforeTurnStart: rateLimitsRevisionBeforeLastTurnStart,
|
|
9571
7472
|
timeoutMs: appServer.requestTimeoutMs,
|
|
9572
7473
|
signal: runAbortController.signal
|
|
9573
7474
|
});
|
|
9574
7475
|
const turnStartErrorMessage = usageLimitError?.message ?? formatErrorMessage(turnStartError);
|
|
9575
|
-
if (isInvalidCodexImagePayloadError(turnStartErrorMessage)) await clearCodexBindingAfterInvalidImagePayload(
|
|
7476
|
+
if (isInvalidCodexImagePayloadError(turnStartErrorMessage)) await clearCodexBindingAfterInvalidImagePayload(bindingStore, bindingIdentity, {
|
|
9576
7477
|
phase: "turn_start",
|
|
9577
7478
|
threadId: thread.threadId,
|
|
9578
7479
|
error: turnStartErrorMessage
|
|
@@ -9634,8 +7535,8 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
9634
7535
|
threadId: thread.threadId,
|
|
9635
7536
|
timeoutMs: CODEX_APP_SERVER_UNSUBSCRIBE_TIMEOUT_MS
|
|
9636
7537
|
});
|
|
9637
|
-
|
|
9638
|
-
|
|
7538
|
+
releaseCurrentRoute();
|
|
7539
|
+
activateNativePreToolUseFailureFallback();
|
|
9639
7540
|
nativeHookRelay?.unregister();
|
|
9640
7541
|
await releaseSandboxExecEnvironment();
|
|
9641
7542
|
await runAgentCleanupStep({
|
|
@@ -9666,6 +7567,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
9666
7567
|
}
|
|
9667
7568
|
}
|
|
9668
7569
|
if (!turn) {
|
|
7570
|
+
activateNativePreToolUseFailureFallback();
|
|
9669
7571
|
await releaseSharedClientLeaseAndRetireOneShotClient();
|
|
9670
7572
|
throw new Error("codex app-server turn/start failed without an error");
|
|
9671
7573
|
}
|
|
@@ -9697,27 +7599,12 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
9697
7599
|
}
|
|
9698
7600
|
}, thread.threadId, activeTurnId, {
|
|
9699
7601
|
nativePostToolUseRelayEnabled: nativeHookRelay?.allowedEvents.includes("post_tool_use") === true && nativeHookRelay.shouldRelayEvent("post_tool_use"),
|
|
7602
|
+
readRecentRateLimits: () => readRecentCodexRateLimits(client),
|
|
7603
|
+
runAbortSignal: runAbortController.signal,
|
|
9700
7604
|
trajectoryRecorder,
|
|
9701
7605
|
onNativeToolResultRecorded: maybeAnnounceFastModeAutoOff
|
|
9702
7606
|
});
|
|
9703
|
-
if (isTerminalTurnStatus(turn.turn.status)
|
|
9704
|
-
const closeCleanup = client.addCloseHandler?.(() => {
|
|
9705
|
-
if (completed || terminalTurnNotificationQueued || runAbortController.signal.aborted) return;
|
|
9706
|
-
clientClosedPromptError = "codex app-server client closed before turn completed";
|
|
9707
|
-
trajectoryRecorder?.recordEvent("turn.client_closed", {
|
|
9708
|
-
threadId: thread.threadId,
|
|
9709
|
-
turnId: activeTurnId
|
|
9710
|
-
});
|
|
9711
|
-
embeddedAgentLog.warn("codex app-server client closed before turn completed", {
|
|
9712
|
-
threadId: thread.threadId,
|
|
9713
|
-
turnId: activeTurnId
|
|
9714
|
-
});
|
|
9715
|
-
clientClosedAbort = true;
|
|
9716
|
-
runAbortController.abort("client_closed");
|
|
9717
|
-
completed = true;
|
|
9718
|
-
turnWatches.clearAllTimers();
|
|
9719
|
-
resolveCompletion?.();
|
|
9720
|
-
});
|
|
7607
|
+
if (isTerminalTurnStatus(turn.turn.status)) terminalTurnNotificationQueued = true;
|
|
9721
7608
|
emitLifecycleStart();
|
|
9722
7609
|
const activeProjector = projectorRef.current;
|
|
9723
7610
|
if (!activeProjector) throw new Error("codex app-server projector was not initialized");
|
|
@@ -9725,7 +7612,17 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
9725
7612
|
turnWatches.touchActivity("turn:start", { arm: true });
|
|
9726
7613
|
turnWatches.armAttemptIdleWatch();
|
|
9727
7614
|
turnWatches.touchActivity("turn:start", { attemptProgress: true });
|
|
9728
|
-
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
|
+
}
|
|
9729
7626
|
if (!completed && isTerminalTurnStatus(turn.turn.status)) await enqueueNotification({
|
|
9730
7627
|
method: "turn/completed",
|
|
9731
7628
|
params: {
|
|
@@ -9733,6 +7630,9 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
9733
7630
|
turnId: activeTurnId,
|
|
9734
7631
|
turn: turn.turn
|
|
9735
7632
|
}
|
|
7633
|
+
}, {
|
|
7634
|
+
threadId: thread.threadId,
|
|
7635
|
+
turnId: activeTurnId
|
|
9736
7636
|
});
|
|
9737
7637
|
const activeSteeringQueue = createCodexSteeringQueue({
|
|
9738
7638
|
client,
|
|
@@ -9774,7 +7674,10 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
9774
7674
|
const abortListener = () => {
|
|
9775
7675
|
if (timedOut) {
|
|
9776
7676
|
(async () => {
|
|
9777
|
-
await
|
|
7677
|
+
await bindingStore.mutate(bindingIdentity, {
|
|
7678
|
+
kind: "clear",
|
|
7679
|
+
threadId: thread.threadId
|
|
7680
|
+
});
|
|
9778
7681
|
await retireCodexAppServerClientAfterTimedOutTurn(client, {
|
|
9779
7682
|
threadId: thread.threadId,
|
|
9780
7683
|
turnId: activeTurnId,
|
|
@@ -9822,7 +7725,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
9822
7725
|
const clientClosedPromptErrorForFinal = clientClosedPromptError && hasRecoverableCompletedAssistant ? void 0 : clientClosedPromptError;
|
|
9823
7726
|
let finalPromptError = clientClosedPromptErrorForFinal ?? (effectiveTurnCompletionIdleTimedOut ? turnCompletionIdleTimeoutMessage : effectiveTimedOut ? "codex app-server attempt timed out" : result.promptError);
|
|
9824
7727
|
const finalPromptErrorMessage = typeof finalPromptError === "string" ? finalPromptError : finalPromptError ? formatErrorMessage(finalPromptError) : void 0;
|
|
9825
|
-
if (isInvalidCodexImagePayloadError(finalPromptErrorMessage)) await clearCodexBindingAfterInvalidImagePayload(
|
|
7728
|
+
if (isInvalidCodexImagePayloadError(finalPromptErrorMessage)) await clearCodexBindingAfterInvalidImagePayload(bindingStore, bindingIdentity, {
|
|
9826
7729
|
phase: "turn_completed",
|
|
9827
7730
|
threadId: thread.threadId,
|
|
9828
7731
|
turnId: activeTurnId,
|
|
@@ -9838,9 +7741,10 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
9838
7741
|
turnId: activeTurnId,
|
|
9839
7742
|
error: finalPromptErrorMessage
|
|
9840
7743
|
});
|
|
9841
|
-
|
|
9842
|
-
|
|
9843
|
-
|
|
7744
|
+
await bindingStore.mutate(bindingIdentity, {
|
|
7745
|
+
kind: "clear",
|
|
7746
|
+
threadId: thread.threadId
|
|
7747
|
+
});
|
|
9844
7748
|
}
|
|
9845
7749
|
const refreshedUsageLimitPromptError = await refreshCodexUsageLimitPromptError({
|
|
9846
7750
|
client,
|
|
@@ -9896,7 +7800,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
9896
7800
|
if (modelCallFailureKind) codexModelCallDiagnostics.emitError(finalPromptError ?? "codex app-server attempt interrupted", { failureKind: modelCallFailureKind });
|
|
9897
7801
|
else if (finalPromptError) codexModelCallDiagnostics.emitError(finalPromptError);
|
|
9898
7802
|
else codexModelCallDiagnostics.emitCompleted(result);
|
|
9899
|
-
await mirrorTranscriptBestEffort({
|
|
7803
|
+
const assistantTranscriptOwned = await mirrorTranscriptBestEffort({
|
|
9900
7804
|
params,
|
|
9901
7805
|
agentId: sessionAgentId,
|
|
9902
7806
|
notifyUserMessagePersisted,
|
|
@@ -9973,17 +7877,14 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
9973
7877
|
shouldDelayNativeHookRelayUnregister = completedTurnStatus === "completed" && !effectiveTimedOut && !runAbortController.signal.aborted && !finalAborted && !finalPromptError;
|
|
9974
7878
|
if (shouldDelayNativeHookRelayUnregister) try {
|
|
9975
7879
|
await markCodexAppServerBindingCoveredThroughTurn({
|
|
9976
|
-
|
|
9977
|
-
|
|
9978
|
-
|
|
9979
|
-
agentDir: params.agentDir,
|
|
9980
|
-
config: params.config
|
|
7880
|
+
bindingStore,
|
|
7881
|
+
identity: bindingIdentity,
|
|
7882
|
+
threadId: thread.threadId
|
|
9981
7883
|
});
|
|
9982
7884
|
} catch (error) {
|
|
9983
|
-
if (!await
|
|
9984
|
-
|
|
9985
|
-
|
|
9986
|
-
config: params.config
|
|
7885
|
+
if (!await bindingStore.mutate(bindingIdentity, {
|
|
7886
|
+
kind: "clear",
|
|
7887
|
+
threadId: thread.threadId
|
|
9987
7888
|
})) throw error;
|
|
9988
7889
|
embeddedAgentLog.warn("codex app-server binding coverage update failed after completed turn; cleared stale binding", {
|
|
9989
7890
|
threadId: thread.threadId,
|
|
@@ -10015,11 +7916,18 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
10015
7916
|
});
|
|
10016
7917
|
if (finalPromptError) emitLifecycleTerminal({
|
|
10017
7918
|
phase: "error",
|
|
10018
|
-
error: formatErrorMessage(finalPromptError)
|
|
7919
|
+
error: formatErrorMessage(finalPromptError),
|
|
7920
|
+
...buildLifecycleTerminalMeta({
|
|
7921
|
+
aborted: finalAborted,
|
|
7922
|
+
timedOut: effectiveTimedOut
|
|
7923
|
+
})
|
|
10019
7924
|
});
|
|
10020
7925
|
else emitLifecycleTerminal({
|
|
10021
7926
|
phase: "end",
|
|
10022
|
-
...
|
|
7927
|
+
...buildLifecycleTerminalMeta({
|
|
7928
|
+
aborted: finalAborted,
|
|
7929
|
+
timedOut: effectiveTimedOut
|
|
7930
|
+
})
|
|
10023
7931
|
});
|
|
10024
7932
|
return {
|
|
10025
7933
|
...result,
|
|
@@ -10029,6 +7937,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
10029
7937
|
promptErrorSource: finalPromptErrorSource,
|
|
10030
7938
|
...codexAppServerFailure ? { codexAppServerFailure } : {},
|
|
10031
7939
|
...promptTimeoutOutcome ? { promptTimeoutOutcome } : {},
|
|
7940
|
+
...assistantTranscriptOwned ? { assistantTranscriptOwned: true } : {},
|
|
10032
7941
|
systemPromptReport
|
|
10033
7942
|
};
|
|
10034
7943
|
} finally {
|
|
@@ -10036,7 +7945,11 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
10036
7945
|
codexModelCallDiagnostics.emitError("codex app-server run completed without model-call terminal event");
|
|
10037
7946
|
emitLifecycleTerminal({
|
|
10038
7947
|
phase: "error",
|
|
10039
|
-
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
|
+
})
|
|
10040
7953
|
});
|
|
10041
7954
|
if (trajectoryRecorder && !trajectoryEndRecorded) trajectoryRecorder.recordEvent("session.ended", {
|
|
10042
7955
|
status: timedOut || runAbortController.signal.aborted && !clientClosedAbort ? "interrupted" : "cleanup",
|
|
@@ -10055,16 +7968,21 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
10055
7968
|
}
|
|
10056
7969
|
});
|
|
10057
7970
|
if (!timedOut && !runAbortController.signal.aborted) await steeringQueueRef.current?.flushPending();
|
|
10058
|
-
|
|
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, {
|
|
10059
7979
|
threadId: thread.threadId,
|
|
10060
7980
|
timeoutMs: CODEX_APP_SERVER_UNSUBSCRIBE_TIMEOUT_MS
|
|
10061
7981
|
});
|
|
10062
7982
|
userInputBridgeRef.current?.cancelPending();
|
|
10063
7983
|
turnWatches.clearAllTimers();
|
|
10064
|
-
|
|
10065
|
-
|
|
10066
|
-
closeCleanup?.();
|
|
10067
|
-
await releaseSharedClientLeaseAndRetireOneShotClient();
|
|
7984
|
+
releaseCurrentRoute();
|
|
7985
|
+
if (!yieldedOneShotCleanupDeferred) await releaseSharedClientLeaseAndRetireOneShotClient();
|
|
10068
7986
|
if (nativeHookRelay) if (shouldDelayNativeHookRelayUnregister) scheduleCodexNativeHookRelayUnregister({
|
|
10069
7987
|
relay: nativeHookRelay,
|
|
10070
7988
|
hookTimeoutSec: options.nativeHookRelay?.hookTimeoutSec
|
|
@@ -10081,9 +7999,11 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
10081
7999
|
function readDynamicToolCallParams(value) {
|
|
10082
8000
|
return readCodexDynamicToolCallParams(value);
|
|
10083
8001
|
}
|
|
10084
|
-
async function clearCodexBindingAfterInvalidImagePayload(
|
|
10085
|
-
const currentBinding = await
|
|
10086
|
-
|
|
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) {
|
|
10087
8007
|
embeddedAgentLog.warn("codex app-server image payload error detected for unbound thread; preserving thread binding", {
|
|
10088
8008
|
...fields,
|
|
10089
8009
|
boundThreadId: currentBinding.threadId
|
|
@@ -10091,32 +8011,21 @@ async function clearCodexBindingAfterInvalidImagePayload(sessionFile, fields) {
|
|
|
10091
8011
|
return;
|
|
10092
8012
|
}
|
|
10093
8013
|
embeddedAgentLog.warn("codex app-server image payload error detected; clearing thread binding", fields);
|
|
10094
|
-
await
|
|
8014
|
+
await bindingStore.mutate(identity, {
|
|
8015
|
+
kind: "clear",
|
|
8016
|
+
threadId: expectedThreadId
|
|
8017
|
+
});
|
|
10095
8018
|
}
|
|
10096
8019
|
async function markCodexAppServerBindingCoveredThroughTurn(params) {
|
|
10097
|
-
|
|
10098
|
-
|
|
10099
|
-
|
|
10100
|
-
|
|
10101
|
-
});
|
|
10102
|
-
if (!currentBinding || currentBinding.threadId !== params.threadId) return;
|
|
10103
|
-
const { schemaVersion: _schemaVersion, sessionFile: _boundSessionFile, updatedAt: _updatedAt, ...bindingForWrite } = currentBinding;
|
|
10104
|
-
await writeCodexAppServerBinding(params.sessionFile, bindingForWrite, {
|
|
10105
|
-
authProfileStore: params.authProfileStore,
|
|
10106
|
-
agentDir: params.agentDir,
|
|
10107
|
-
config: params.config
|
|
8020
|
+
await params.bindingStore.mutate(params.identity, {
|
|
8021
|
+
kind: "patch",
|
|
8022
|
+
threadId: params.threadId,
|
|
8023
|
+
patch: { historyCoveredThrough: (/* @__PURE__ */ new Date()).toISOString() }
|
|
10108
8024
|
});
|
|
10109
8025
|
}
|
|
10110
8026
|
function isNonEmptyString(value) {
|
|
10111
8027
|
return typeof value === "string" && value.length > 0;
|
|
10112
8028
|
}
|
|
10113
|
-
function canAttributeUnscopedNativeResponseDeltaToThisTurn(client) {
|
|
10114
|
-
const activeLeases = client.getActiveSharedLeaseCountForUnscopedNotifications?.();
|
|
10115
|
-
return activeLeases === void 0 || activeLeases <= 1;
|
|
10116
|
-
}
|
|
10117
|
-
function isUnscopedCodexNotification(correlation) {
|
|
10118
|
-
return !correlation.threadId && !correlation.turnId && !correlation.nestedTurnThreadId && !correlation.nestedTurnId;
|
|
10119
|
-
}
|
|
10120
8029
|
function shouldUseFreshCodexThreadAfterContextEngineOverflow(params) {
|
|
10121
8030
|
if (!params.contextEngineActive || params.thread.lifecycle.action !== "resumed") return false;
|
|
10122
8031
|
return isCodexContextWindowError(params.error);
|
|
@@ -10131,13 +8040,6 @@ function isCodexActiveCompactTurnError(error) {
|
|
|
10131
8040
|
const codexErrorInfo = isJsonObject(data?.codexErrorInfo) ? data.codexErrorInfo : void 0;
|
|
10132
8041
|
return (isJsonObject(codexErrorInfo?.activeTurnNotSteerable) ? codexErrorInfo.activeTurnNotSteerable : void 0)?.turnKind === "compact";
|
|
10133
8042
|
}
|
|
10134
|
-
function isCodexThreadTurnCompletedNotification(notification, threadId, turnIds) {
|
|
10135
|
-
if (notification.method !== "turn/completed") return false;
|
|
10136
|
-
const correlation = describeCodexNotificationCorrelation(notification, { threadId });
|
|
10137
|
-
if (!correlation.matchesActiveThread) return false;
|
|
10138
|
-
const turnId = correlation.turnId ?? correlation.nestedTurnId;
|
|
10139
|
-
return !turnIds || turnId !== void 0 && turnIds.has(turnId);
|
|
10140
|
-
}
|
|
10141
8043
|
function readCodexFinalizationHookNotification(notification, threadId, turnId) {
|
|
10142
8044
|
if (notification.method !== "hook/started" && notification.method !== "hook/completed") return;
|
|
10143
8045
|
const params = isJsonObject(notification.params) ? notification.params : void 0;
|
|
@@ -10207,12 +8109,15 @@ function handleApprovalRequest(params) {
|
|
|
10207
8109
|
execReviewerAgentId: params.execReviewerAgentId,
|
|
10208
8110
|
internalExecAutoReview: params.internalExecAutoReview,
|
|
10209
8111
|
autoApprove: params.autoApprove,
|
|
10210
|
-
signal: params.signal
|
|
8112
|
+
signal: params.signal,
|
|
8113
|
+
onNativeToolFailureDisposition: params.onNativeToolFailureDisposition
|
|
10211
8114
|
});
|
|
10212
8115
|
}
|
|
10213
8116
|
function resolveCodexDynamicToolDirectNames(params) {
|
|
10214
|
-
|
|
10215
|
-
|
|
8117
|
+
const names = [];
|
|
8118
|
+
if (params.crestodianTool) names.push("crestodian");
|
|
8119
|
+
if (params.sourceReplyDeliveryMode === "message_tool_only") names.push("message");
|
|
8120
|
+
return names;
|
|
10216
8121
|
}
|
|
10217
8122
|
//#endregion
|
|
10218
8123
|
export { runCodexAppServerAttempt };
|