@openclaw/codex 2026.7.2-beta.2 → 2026.7.2-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-Scc-Wevo.js → app-server-policy-BO3jYBkv.js} +1 -1
- package/dist/{attempt-notifications-BFmNhqFl.js → attempt-notifications-DdmnBMUp.js} +1 -1
- package/dist/{media-understanding-provider-D31dOJwb.js → bounded-turn-CaQL96kg.js} +43 -183
- package/dist/capabilities-CLICvTxf.js +36 -0
- package/dist/cli-metadata.js +1 -1
- package/dist/{command-formatters-Dq9InZUK.js → command-formatters-DgADqnHa.js} +4 -34
- package/dist/{command-handlers-IgqmL_kv.js → command-handlers-l_lVPNlB.js} +72 -13
- package/dist/{compact-DCTmg3id.js → compact-CC5KhBzX.js} +5 -4
- package/dist/{computer-use-CPcU8TjG.js → computer-use-Ip1-QGa-.js} +4 -4
- package/dist/{conversation-control-CGkBlfc5.js → conversation-control-sNPsbTCP.js} +6 -5
- package/dist/doctor-contract-api.js +1 -1
- package/dist/{dynamic-tools-C_1tEs34.js → dynamic-tools-CQI3UwqY.js} +117 -2543
- package/dist/event-projector-assistant-message-DTgnPPVF.js +101 -0
- package/dist/event-projector-usage-D6tE7_qD.js +69 -0
- package/dist/harness.js +27 -7
- package/dist/incognito-session-ET_ufDH-.js +2 -0
- package/dist/index.js +60 -26
- package/dist/media-understanding-provider.js +168 -1
- package/dist/{models-uh26C8QU.js → models-BNeMlHgg.js} +2 -2
- package/dist/{notification-correlation-KmfV4EkP.js → notification-correlation-BGLixv5_.js} +3 -3
- package/dist/{plugin-app-cache-key-Ceb-lm2c.js → plugin-app-cache-key-CvzvmBRz.js} +2 -2
- package/dist/{rate-limits-DyXaYAxU.js → rate-limits-D0IJT-a6.js} +3 -1
- package/dist/{request-B_oQsCXy.js → request-BTccUTD3.js} +1 -1
- package/dist/{run-attempt-MLzoMe_m.js → run-attempt--858NXDl.js} +2798 -172
- package/dist/{session-binding-h1mmCGnl.js → session-binding-ZSMX1omP.js} +9 -2
- package/dist/{session-catalog-CEvoXWHA.js → session-catalog-DsKaezgR.js} +119 -96
- package/dist/{session-cli-XsEuWb_C.js → session-cli-CYJTfRLG.js} +2 -2
- package/dist/session-upstream-marker-D7XlI9us.js +82 -0
- package/dist/settled-turn-finalizer-DHltNgtF.js +303 -0
- package/dist/{shared-client-JiAnW6pc.js → shared-client-wmgNFNYp.js} +82 -13
- package/dist/{side-question-DqDvIwSU.js → side-question-BfFZCsJ-.js} +21 -15
- package/dist/{thread-lifecycle-CN_pPtPh.js → thread-lifecycle-cqrOlJkq.js} +157 -32
- package/dist/{transcript-mirror-D9rTxx2P.js → transcript-mirror-2p7kl6e4.js} +433 -145
- package/dist/upstream-session-fork-vPjwkT1i.js +320 -0
- package/dist/{usage-D5Rohxoc.js → usage-DM_I3mPT.js} +3 -3
- package/dist/{web-search-provider.runtime-CKVBn3fP.js → web-search-provider.runtime-Bx6HAc_k.js} +3 -3
- package/npm-shrinkwrap.json +38 -38
- package/openclaw.plugin.json +11 -1
- package/package.json +7 -7
|
@@ -1,32 +1,43 @@
|
|
|
1
|
-
import { A as isCodexAppServerStartupError, B as isCodexAppServerApprovalRequest, C as resolveCodexAppServerFallbackApiKeyCacheKey, D as resolveCodexAppServerHomeDir, F as resolveCodexTurnCompletionIdleTimeoutMs, H as isCodexAppServerConnectionClosedError, I as resolveCodexTurnTerminalIdleTimeoutMs, K as isCodexAppServerRequestTimeoutError, L as withCodexStartupTimeout, M as resolveCodexPostToolRawAssistantCompletionIdleTimeoutMs, N as resolveCodexStartupTimeoutMs, O as CODEX_POST_REASONING_REPLY_IDLE_TIMEOUT_MS, P as resolveCodexTurnAssistantCompletionIdleTimeoutMs, R as CodexAppServerRpcError, S as resolveCodexAppServerAuthProfileIdForAgent, T as resolveCodexAppServerPreparedAuthHandoff, V as isCodexAppServerBrokenPipeError,
|
|
2
|
-
import { S as updateActiveTurnItemIds, _ as readCodexNotificationItem, a as isCodexTurnAbortMarkerNotification, b as shouldDisarmAssistantCompletionIdleWatch, c as isPendingOpenClawDynamicToolCompletionNotification, d as isRawReasoningCompletionNotification, f as isRawToolOutputCompletionNotification, g as isTerminalTurnStatus, h as isRetryableErrorNotification, i as isAssistantCompletionReleaseNotification, l as isRawAssistantProgressNotification, m as isReasoningProgressNotification, n as describeNotificationActivity, o as isFileChangePatchUpdatedNotification, p as isReasoningItemCompletionNotification, r as isAssistantCommentaryCompletionNotification, s as isNativeToolProgressNotification, t as codexExecutionToolName, u as isRawFunctionToolOutputCompletionNotification, v as readNotificationItemId, x as updateActiveCompletionBlockerItemIds, y as readRawResponseToolCallId } from "./attempt-notifications-
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { r as
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
1
|
+
import { A as isCodexAppServerStartupError, B as isCodexAppServerApprovalRequest, C as resolveCodexAppServerFallbackApiKeyCacheKey, D as resolveCodexAppServerHomeDir, F as resolveCodexTurnCompletionIdleTimeoutMs, H as isCodexAppServerConnectionClosedError, I as resolveCodexTurnTerminalIdleTimeoutMs, K as isCodexAppServerRequestTimeoutError, L as withCodexStartupTimeout, M as resolveCodexPostToolRawAssistantCompletionIdleTimeoutMs, N as resolveCodexStartupTimeoutMs, O as CODEX_POST_REASONING_REPLY_IDLE_TIMEOUT_MS, P as resolveCodexTurnAssistantCompletionIdleTimeoutMs, Q as isJsonObject, R as CodexAppServerRpcError, S as resolveCodexAppServerAuthProfileIdForAgent, T as resolveCodexAppServerPreparedAuthHandoff, V as isCodexAppServerBrokenPipeError, Z as flattenCodexDynamicToolFunctions, _ as readCodexRateLimitsRevision, a as getLeasedSharedCodexAppServerClient, b as resolveCodexAppServerAuthAccountCacheKey, c as releaseLeasedSharedCodexAppServerClient, d as retireSharedCodexAppServerClientIfCurrent, g as ensureCodexAppServerClientRuntime, k as CodexAppServerStartupError, n as clearSharedCodexAppServerClientIfCurrentAndUnclaimed, o as isCodexAppServerStartSelectionChangedError, t as clearSharedCodexAppServerClientIfCurrent, u as retainSharedCodexAppServerClientIfCurrent, v as readRecentCodexRateLimits, w as resolveCodexAppServerPreparedApiKeyCacheKey, x as resolveCodexAppServerAuthProfileId, y as rememberCodexRateLimitsRead } from "./shared-client-wmgNFNYp.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-DdmnBMUp.js";
|
|
3
|
+
import { A as withMcpElicitationsApprovalPolicy, E as resolveCodexPluginsPolicy, O as resolveOpenClawExecPolicyForCodexAppServer, T as resolveCodexModelBackedReviewerPolicyContext, _ as isCodexAppServerApprovalPolicyAllowedByRequirements, b as readCodexPluginConfig, c as reclaimCurrentCodexSessionGeneration, i as createCodexSessionGenerationSupersededError, k as shouldAutoApproveCodexAppServerApprovals, l as sessionBindingIdentity, w as resolveCodexComputerUseConfig, y as isCodexSandboxExecServerEnabled } from "./session-binding-ZSMX1omP.js";
|
|
4
|
+
import { $ as shouldBuildCodexPluginThreadConfig, B as codexDynamicToolsFingerprint, F as closeCodexStartupClientBestEffort, G as projectContextEngineAssemblyForCodex, H as buildContextEngineBinding, I as interruptCodexTurnBestEffort, K as resolveCodexContextEngineProjectionMaxChars, L as retireCodexAppServerClientAfterTimedOutTurn, M as CODEX_APP_SERVER_INTERRUPT_TIMEOUT_MS, N as CODEX_APP_SERVER_UNSUBSCRIBE_TIMEOUT_MS, O as isSystemAgentOnlyCodexDynamicToolAllowlist, P as CodexAppServerUnsafeSubscriptionError, Q as mergeCodexThreadConfigs, R as unsubscribeCodexThreadBestEffort, T as filterCodexDynamicTools, U as isContextEngineBindingCompatible, V as codexLegacyDynamicToolsFingerprint, W as fitCodexProjectedContextForTurnStart, X as buildCodexPluginThreadConfigInputFingerprint, Y as buildCodexPluginThreadConfig, Z as buildCodexPluginThreadConfigTimeoutFallback, a as sanitizeCodexHistoryImagePayloads, at as readCodexErrorNotificationMessage, b as resolveCodexAppServerThreadModelSelection, ct as readItemString, dt as readNonNegativeInteger, ft as readNullableString$1, g as buildDeveloperInstructions, h as resolveCodexWebSearchPlan, ht as splitPlanText, it as normalizeNonEmptyString, j as resolveCodexDynamicToolsLoadingForRuntime, lt as readNonEmptyString$1, mt as readString$5, n as buildTurnStartParams, nt as resolveRecoverableCodexPluginConfigKeys, ot as readHookOutputEntries, pt as readNumber, q as resolveCodexContextEngineProjectionReserveTokens, r as buildCodexUserInput, rt as extractRawAssistantText, s as startOrResumeThread, st as readItem, t as buildTurnCollaborationMode, ut as readNonEmptyStringArray, w as isCodexAppServerProfilerEnabled, z as areCodexDynamicToolFingerprintsCompatible } from "./thread-lifecycle-cqrOlJkq.js";
|
|
5
|
+
import { n as projectCodexThreadUsageUpdate, t as normalizeCodexResponseTokenUsage } from "./event-projector-usage-D6tE7_qD.js";
|
|
6
|
+
import { C as readCodexTurnCompletedNotification, S as readCodexTurn, d as attachCodexMirrorIdentity, f as readMirrorIdentity, i as mirrorPromptAtTurnStartBestEffort, n as createCodexAppServerUserMessagePersistenceNotifier, o as buildCodexUserPromptMessage, s as promptSnapshot, t as codexTranscriptMirrorRuntime, u as serializeCodexMirrorSourceEvidence, v as assertCodexTurnStartResponse, y as readCodexDynamicToolCallParams } from "./transcript-mirror-2p7kl6e4.js";
|
|
7
|
+
import { i as defaultCodexAppInventoryCache, n as buildCodexAppServerRuntimeFingerprint, r as buildCodexPluginAppCacheKey } from "./plugin-app-cache-key-CvzvmBRz.js";
|
|
8
|
+
import { t as isIncognitoSessionKey } from "./incognito-session-ET_ufDH-.js";
|
|
9
|
+
import { t as CODEX_CONTROL_METHODS } from "./capabilities-CLICvTxf.js";
|
|
10
|
+
import { a as shouldRefreshCodexRateLimitsForUsageLimitMessage, i as resolveCodexUsageLimitResetAtMs, n as formatCodexUsageLimitErrorMessage } from "./rate-limits-D0IJT-a6.js";
|
|
11
|
+
import { r as formatCodexDisplayText } from "./command-formatters-DgADqnHa.js";
|
|
12
|
+
import { n as resolveCodexAppServerForOpenClawToolPolicy, t as resolveCodexAppServerForModelProvider } from "./app-server-policy-BO3jYBkv.js";
|
|
13
|
+
import { a as resolveCodexBindingAppServerConnection, n as readCodexNotificationThreadId, r as readCodexNotificationTurnId, t as isCodexNotificationForTurn } from "./notification-correlation-BGLixv5_.js";
|
|
10
14
|
import { n as prepareCodexAppServerAuthBinding } from "./auth-binding-C1czuHPA.js";
|
|
11
|
-
import {
|
|
12
|
-
import { a as
|
|
15
|
+
import { i as resolveCodexLocalRuntimeAttribution, n as createAssistantMessage, r as createAssistantMirrorMessage, t as createAssistantCommentaryMessage } from "./event-projector-assistant-message-DTgnPPVF.js";
|
|
16
|
+
import { $ as shouldSynthesizeToolProgressForItem, A as resolveCodexNativeHookRelayEvents, B as shouldReleaseTurnAfterTerminalDynamicTool, C as CodexNativeToolLifecycleProjector, D as buildCodexNativeHookRelayDisabledConfig, E as buildCodexNativeHookRelayConfig, F as isDynamicToolTerminalDiagnosticEvent, G as isNonSuccessItemStatus, H as toCodexDynamicToolProtocolResponse, I as isMatchingDynamicToolTerminalDiagnostic, J as itemName, K as isSideEffectingNativeToolItem, L as resolveDynamicToolCallTimeoutMs, M as scheduleCodexNativeHookRelayUnregister, N as handleDynamicToolCallWithTimeout, O as createCodexNativeHookRelay, P as hasPendingDynamicToolTerminalDiagnostic, Q as shouldRecordNativeToolTranscript, R as resolveTerminalDynamicToolBatchAction, T as CODEX_NATIVE_HOOK_RELAY_TTL_GRACE_MS, U as resolveCodexToolAbortTerminalReason, V as toCodexDynamicToolProgressResponse, W as isMutatingNativeToolItem, X as itemTitle, Y as itemStatus, Z as shouldClearTerminalPresentationForNativeItem, _ as resolveCodexMessageToolProvider, a as emitDynamicToolStartedDiagnostic, b as shouldRequireCodexSandboxExecServerEnvironment, d as createCodexDynamicToolBuildStageTracker, et as unknownItemStatus, f as disableCodexPluginThreadConfig, g as resolveCodexExternalSandboxPolicyForOpenClawSandbox, h as resolveCodexAppServerHookChannelId, i as emitDynamicToolErrorDiagnostic, j as resolveCodexNativeHookRelayTtlMs, k as emitCodexNativePreToolUseFailureDiagnostic, l as settleCodexSourceReplyFinality, m as resolveCodexAppServerExecutionCwd, n as handleCodexAppServerApprovalRequest, o as emitDynamicToolTerminalDiagnostic, p as formatCodexDynamicToolBuildStageSummary, q as itemKind, r as handleCodexAppServerElicitationRequest, s as resolveCodexProviderWebSearchSupport, t as createCodexDynamicToolBridge, u as buildDynamicTools, v as resolveCodexSandboxEnvironmentSelection, x as shouldWarnCodexDynamicToolBuildStageSummary, y as shouldEnableCodexAppServerNativeToolSurface, z as shouldBlockTerminalReleaseForNonTerminalDynamicToolResult } from "./dynamic-tools-CQI3UwqY.js";
|
|
17
|
+
import { a as runCodexComputerUseLiveTest, r as killStaleComputerUseMcpChildren, t as ensureCodexComputerUse } from "./computer-use-Ip1-QGa-.js";
|
|
13
18
|
import fs from "node:fs/promises";
|
|
14
19
|
import path, { posix } from "node:path";
|
|
15
|
-
import { resolveTimerTimeoutMs } from "openclaw/plugin-sdk/number-runtime";
|
|
20
|
+
import { asDateTimestampMs, resolveTimerTimeoutMs } from "openclaw/plugin-sdk/number-runtime";
|
|
16
21
|
import { sliceUtf16Safe, truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
17
22
|
import { createHash, randomUUID } from "node:crypto";
|
|
18
|
-
import { resolveAgentDir, resolveAgentWorkspaceDir } from "openclaw/plugin-sdk/agent-runtime";
|
|
23
|
+
import { markAuthProfileBlockedUntil, resolveAgentDir, resolveAgentWorkspaceDir } from "openclaw/plugin-sdk/agent-runtime";
|
|
19
24
|
import { loadExecApprovals } from "openclaw/plugin-sdk/exec-approvals-runtime";
|
|
20
25
|
import { asFiniteNumber, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
21
|
-
import { CODEX_APP_SERVER_CONTEXT_ENGINE_HOST, FAST_MODE_AUTO_PROGRESS_KIND, assembleHarnessContextEngine, assertContextEngineHostSupport, awaitAgentEndSideEffects, bootstrapHarnessContextEngine, buildAgentHarnessUserInputAnswers, buildBootstrapContextForFiles, buildHarnessContextEngineRuntimeContext, buildHarnessContextEngineRuntimeContextFromUsage, clearActiveEmbeddedRun, deliverAgentHarnessUserInputPrompt, embeddedAgentLog, emitAgentEvent, emptyAgentHarnessUserInputAnswers, finalizeHarnessContextEngineTurn, formatErrorMessage, formatFastModeAutoProgressText, getAgentHarnessHookRunner, getBeforeToolCallPolicyDiagnosticState, isActiveHarnessContextEngine, isHostScopedAgentToolActive, loadCodexBundleMcpThreadConfig, materializeRequesterScopedMcpToolsForHarnessRun, resolveAgentDir as resolveAgentDir$1, resolveAgentHarnessBeforePromptBuildResult, resolveAgentRunAbortLifecycleFields, resolveBootstrapFilesForRun, resolveContextEngineOwnerPluginId, resolveFastModeForElapsed, resolveSandboxContext, resolveSessionAgentIds as resolveSessionAgentIds$1, resolveUserPath, runAgentCleanupStep, runAgentEndSideEffects, runAgentHarnessLlmInputHook, runAgentHarnessLlmOutputHook, runHarnessContextEngineMaintenance, setActiveEmbeddedRun, supportsModelTools } from "openclaw/plugin-sdk/agent-harness-runtime";
|
|
26
|
+
import { CODEX_APP_SERVER_CONTEXT_ENGINE_HOST, FAST_MODE_AUTO_PROGRESS_KIND, TOOL_PROGRESS_OUTPUT_MAX_CHARS, assembleHarnessContextEngine, assertContextEngineHostSupport, awaitAgentEndSideEffects, bootstrapHarnessContextEngine, buildAgentHarnessUserInputAnswers, buildBootstrapContextForFiles, buildHarnessContextEngineRuntimeContext, buildHarnessContextEngineRuntimeContextFromUsage, callGatewayTool, cancelPendingAgentQuestionForSession, claimPendingAgentQuestionAnswer, classifyAgentHarnessTerminalOutcome, clearActiveEmbeddedRun, deliverAgentHarnessUserInputPrompt, embeddedAgentLog, emitAgentEvent, emptyAgentHarnessUserInputAnswers, finalizeHarnessContextEngineTurn, formatErrorMessage, formatFastModeAutoProgressText, formatToolAggregate, formatToolProgressOutput, getAgentHarnessHookRunner, getBeforeToolCallPolicyDiagnosticState, inferToolMetaFromArgs, isActiveHarnessContextEngine, isHostScopedAgentToolActive, loadCodexBundleMcpThreadConfig, materializeRequesterScopedMcpToolsForHarnessRun, projectAgentHarnessTranscriptMessageForDisplay, resolveAgentDir as resolveAgentDir$1, resolveAgentHarnessBeforePromptBuildResult, resolveAgentRunAbortLifecycleFields, resolveBootstrapFilesForRun, resolveContextEngineOwnerPluginId, resolveFastModeForElapsed, resolveSandboxContext, resolveSessionAgentIds as resolveSessionAgentIds$1, resolveUserPath, runAgentCleanupStep, runAgentEndSideEffects, runAgentHarnessAfterCompactionHook, runAgentHarnessAfterToolCallHook, runAgentHarnessBeforeCompactionHook, runAgentHarnessGatewayQuestion, runAgentHarnessLlmInputHook, runAgentHarnessLlmOutputHook, runHarnessContextEngineMaintenance, setActiveEmbeddedRun, supportsModelTools } from "openclaw/plugin-sdk/agent-harness-runtime";
|
|
22
27
|
import { spawn } from "node:child_process";
|
|
23
28
|
import { once } from "node:events";
|
|
24
29
|
import { isIP } from "node:net";
|
|
25
30
|
import { WebSocketServer } from "ws";
|
|
26
|
-
import { parseSqliteSessionFileMarker } from "openclaw/plugin-sdk/session-store-runtime";
|
|
31
|
+
import { listSessionEntries, parseSqliteSessionFileMarker } from "openclaw/plugin-sdk/session-store-runtime";
|
|
27
32
|
import { fileURLToPath } from "node:url";
|
|
33
|
+
import { redactSensitiveFieldValue, redactSensitiveText, redactToolPayloadText } from "openclaw/plugin-sdk/logging-core";
|
|
28
34
|
import { createDiagnosticTraceContextFromActiveScope, emitTrustedDiagnosticEvent, emitTrustedDiagnosticEventWithPrivateData, freezeDiagnosticTraceContext, onInternalDiagnosticEvent, resolveDiagnosticModelContentCapturePolicy } from "openclaw/plugin-sdk/diagnostic-runtime";
|
|
29
|
-
import {
|
|
35
|
+
import { readSessionTranscriptEvents } from "openclaw/plugin-sdk/session-transcript-runtime";
|
|
36
|
+
import { prepareMemorySystemPromptAddition } from "openclaw/plugin-sdk/core";
|
|
37
|
+
import { sanitizeTerminalText } from "openclaw/plugin-sdk/text-chunking";
|
|
38
|
+
import { buildSessionContext, migrateSessionEntries, parseSessionEntries } from "openclaw/plugin-sdk/agent-sessions";
|
|
39
|
+
import { generatedImageAssetFromBase64 } from "openclaw/plugin-sdk/image-generation";
|
|
40
|
+
import { saveMediaBuffer } from "openclaw/plugin-sdk/media-store";
|
|
30
41
|
import { MESSAGE_TOOL_DELIVERY_HINTS } from "openclaw/plugin-sdk/message-tool-delivery-hints";
|
|
31
42
|
import { createAgentHarnessTaskRuntime, deliverAgentHarnessTaskCompletion, isDurableAgentHarnessCompletionDelivery } from "openclaw/plugin-sdk/agent-harness-task-runtime";
|
|
32
43
|
import { SsrFBlockedError, isBlockedHostnameOrIp } from "openclaw/plugin-sdk/ssrf-runtime";
|
|
@@ -190,41 +201,2467 @@ function normalizeCodexSteerDebounceMs(value) {
|
|
|
190
201
|
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? Math.floor(value) : CODEX_STEER_ALL_DEBOUNCE_MS;
|
|
191
202
|
}
|
|
192
203
|
//#endregion
|
|
193
|
-
//#region extensions/codex/src/app-server/
|
|
204
|
+
//#region extensions/codex/src/app-server/event-projector-assistant.ts
|
|
205
|
+
var CodexAssistantProjection = class {
|
|
206
|
+
constructor(params, emitAgentEvent, matchesToolProgressEcho, nextTranscriptTimestamp) {
|
|
207
|
+
this.params = params;
|
|
208
|
+
this.emitAgentEvent = emitAgentEvent;
|
|
209
|
+
this.matchesToolProgressEcho = matchesToolProgressEcho;
|
|
210
|
+
this.nextTranscriptTimestamp = nextTranscriptTimestamp;
|
|
211
|
+
this.assistantTextByItem = /* @__PURE__ */ new Map();
|
|
212
|
+
this.assistantItemOrder = [];
|
|
213
|
+
this.assistantTimestampByItem = /* @__PURE__ */ new Map();
|
|
214
|
+
this.assistantPhaseByItem = /* @__PURE__ */ new Map();
|
|
215
|
+
this.latestTerminalAssistantCandidateSuperseded = false;
|
|
216
|
+
this.latestTerminalAssistantCandidateCanReleaseAfterToolHandoff = false;
|
|
217
|
+
this.terminalAssistantCandidateEarlierActiveItemIds = /* @__PURE__ */ new Set();
|
|
218
|
+
this.lastCommentaryProgressTextByItem = /* @__PURE__ */ new Map();
|
|
219
|
+
this.lastAnswerCandidateEventByItem = /* @__PURE__ */ new Map();
|
|
220
|
+
this.pendingRawCommentaryEchoes = 0;
|
|
221
|
+
this.rawPromotedAssistantItemIds = /* @__PURE__ */ new Set();
|
|
222
|
+
this.assistantStarted = false;
|
|
223
|
+
this.streamedPartialAssistantItemReplaceable = false;
|
|
224
|
+
}
|
|
225
|
+
hasCompletedTerminalAssistantText(completedItemIds) {
|
|
226
|
+
const latestCompletedItemId = this.latestCompletedTerminalAssistantItemId;
|
|
227
|
+
if (!latestCompletedItemId) return false;
|
|
228
|
+
const finalItem = this.resolveFinalAssistantTextItem();
|
|
229
|
+
return this.latestCompletedItemId === latestCompletedItemId && finalItem?.itemId === latestCompletedItemId && completedItemIds.has(latestCompletedItemId);
|
|
230
|
+
}
|
|
231
|
+
getLatestTerminalAssistantCandidate() {
|
|
232
|
+
const itemId = this.latestTerminalAssistantCandidateItemId;
|
|
233
|
+
if (!itemId) return;
|
|
234
|
+
const text = this.assistantTextByItem.get(itemId)?.trim();
|
|
235
|
+
return {
|
|
236
|
+
itemId,
|
|
237
|
+
hasText: Boolean(text && !this.isToolProgressEchoText(itemId, text))
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
hasLatestTerminalAssistantCandidateText() {
|
|
241
|
+
return !this.latestTerminalAssistantCandidateSuperseded && this.getLatestTerminalAssistantCandidate()?.hasText === true;
|
|
242
|
+
}
|
|
243
|
+
canReleaseLatestTerminalAssistantAfterToolHandoff() {
|
|
244
|
+
return this.latestTerminalAssistantCandidateCanReleaseAfterToolHandoff && this.hasLatestTerminalAssistantCandidateText();
|
|
245
|
+
}
|
|
246
|
+
async handleAssistantDelta(params) {
|
|
247
|
+
const itemId = readString$5(params, "itemId") ?? "assistant";
|
|
248
|
+
const delta = readString$5(params, "delta") ?? "";
|
|
249
|
+
if (!delta) return;
|
|
250
|
+
if (itemId !== this.pendingRawTerminalAssistantEchoItemId) this.pendingRawTerminalAssistantEchoItemId = void 0;
|
|
251
|
+
const isCommentary = this.isCommentaryAssistantItem(itemId);
|
|
252
|
+
if (!isCommentary && itemId !== this.latestTerminalAssistantCandidateItemId) this.markTerminalAssistantCandidateSupersededBy();
|
|
253
|
+
if (!this.assistantStarted) {
|
|
254
|
+
this.assistantStarted = true;
|
|
255
|
+
await this.params.onAssistantMessageStart?.();
|
|
256
|
+
}
|
|
257
|
+
this.rememberAssistantItem(itemId);
|
|
258
|
+
const text = `${this.assistantTextByItem.get(itemId) ?? ""}${delta}`;
|
|
259
|
+
this.assistantTextByItem.set(itemId, text);
|
|
260
|
+
if (isCommentary) {
|
|
261
|
+
this.emitCommentaryProgress({
|
|
262
|
+
itemId,
|
|
263
|
+
text
|
|
264
|
+
});
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
if (this.isFinalAnswerAssistantItem(itemId)) this.emitAnswerCandidate(itemId, "candidate");
|
|
268
|
+
const knownFinalAnswer = this.shouldStreamAssistantPartial(itemId);
|
|
269
|
+
const replace = this.streamedPartialAssistantItemId !== void 0 && this.streamedPartialAssistantItemId !== itemId;
|
|
270
|
+
if (replace && (!knownFinalAnswer || this.streamedPartialAssistantItemReplaceable)) this.streamedPartialAssistantItemReplaceable = true;
|
|
271
|
+
else if (this.streamedPartialAssistantItemId === void 0) this.streamedPartialAssistantItemReplaceable = !knownFinalAnswer;
|
|
272
|
+
this.streamedPartialAssistantItemId = itemId;
|
|
273
|
+
const replaceable = this.streamedPartialAssistantItemReplaceable;
|
|
274
|
+
const replacement = replace && replaceable;
|
|
275
|
+
const streamPayload = {
|
|
276
|
+
text,
|
|
277
|
+
delta: replacement ? "" : delta,
|
|
278
|
+
...replacement ? { replace: true } : {}
|
|
279
|
+
};
|
|
280
|
+
this.emitAgentEvent({
|
|
281
|
+
stream: "assistant",
|
|
282
|
+
data: {
|
|
283
|
+
...streamPayload,
|
|
284
|
+
...replaceable ? { replaceable: true } : {}
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
if (knownFinalAnswer && !replaceable) await this.params.onPartialReply?.(streamPayload);
|
|
288
|
+
}
|
|
289
|
+
recordItemStarted(item, itemId) {
|
|
290
|
+
if (item?.type === "agentMessage" && itemId && itemId !== this.pendingRawTerminalAssistantEchoItemId) this.pendingRawTerminalAssistantEchoItemId = void 0;
|
|
291
|
+
this.rememberAssistantPhase(item);
|
|
292
|
+
if (item?.type === "agentMessage" && itemId) this.rememberAssistantItem(itemId);
|
|
293
|
+
if (itemId && itemId !== this.latestTerminalAssistantCandidateItemId) {
|
|
294
|
+
this.markTerminalAssistantCandidateSupersededBy(itemId, { preserveEarlierActiveItem: true });
|
|
295
|
+
if (this.latestTerminalAssistantCandidateSuperseded) this.pendingRawTerminalAssistantEchoItemId = void 0;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
recordItemCompleted(item, itemId, activeItemIds) {
|
|
299
|
+
if (item?.type === "agentMessage" && itemId && itemId !== this.pendingRawTerminalAssistantEchoItemId) this.pendingRawTerminalAssistantEchoItemId = void 0;
|
|
300
|
+
if (itemId) this.latestCompletedItemId = itemId;
|
|
301
|
+
this.rememberAssistantPhase(item);
|
|
302
|
+
if (item?.type === "agentMessage" && !this.isCommentaryAssistantItem(item.id)) {
|
|
303
|
+
this.latestCompletedTerminalAssistantItemId = item.id;
|
|
304
|
+
this.markLatestTerminalAssistantCandidate(item.id, activeItemIds);
|
|
305
|
+
this.pendingRawTerminalAssistantEchoItemId = item.id;
|
|
306
|
+
} else if (itemId) {
|
|
307
|
+
this.markTerminalAssistantCandidateSupersededBy(itemId, { preserveEarlierActiveItem: true });
|
|
308
|
+
if (this.latestTerminalAssistantCandidateSuperseded) this.pendingRawTerminalAssistantEchoItemId = void 0;
|
|
309
|
+
}
|
|
310
|
+
if (item?.type === "agentMessage" && typeof item.text === "string") {
|
|
311
|
+
this.rememberAssistantItem(item.id);
|
|
312
|
+
this.assistantTextByItem.set(item.id, item.text);
|
|
313
|
+
if (item.text && this.isCommentaryAssistantItem(item.id)) {
|
|
314
|
+
this.emitCommentaryProgress({
|
|
315
|
+
itemId: item.id,
|
|
316
|
+
text: item.text
|
|
317
|
+
});
|
|
318
|
+
this.pendingRawCommentaryEchoes += 1;
|
|
319
|
+
} else if (item.text && this.isFinalAnswerAssistantItem(item.id)) this.emitAnswerCandidate(item.id, "candidate");
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
recordSnapshotItem(item) {
|
|
323
|
+
this.rememberAssistantPhase(item);
|
|
324
|
+
if (item.type === "agentMessage" && typeof item.text === "string") {
|
|
325
|
+
this.rememberAssistantItem(item.id);
|
|
326
|
+
this.assistantTextByItem.set(item.id, item.text);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
handleRawResponseItemCompleted(item, activeItemIds) {
|
|
330
|
+
const role = readString$5(item, "role");
|
|
331
|
+
const phase = readString$5(item, "phase");
|
|
332
|
+
const rawItemId = readString$5(item, "id");
|
|
333
|
+
const candidateWasSupersededBeforeRaw = this.latestTerminalAssistantCandidateSuperseded;
|
|
334
|
+
const pendingTerminalAssistantEchoItemId = this.pendingRawTerminalAssistantEchoItemId;
|
|
335
|
+
const isPendingTerminalAssistantEcho = role === "assistant" && phase !== "commentary" && pendingTerminalAssistantEchoItemId !== void 0 && (rawItemId === void 0 || rawItemId === pendingTerminalAssistantEchoItemId);
|
|
336
|
+
if (pendingTerminalAssistantEchoItemId !== void 0 && !isPendingTerminalAssistantEcho) this.pendingRawTerminalAssistantEchoItemId = void 0;
|
|
337
|
+
if (!isPendingTerminalAssistantEcho) {
|
|
338
|
+
this.latestCompletedItemId = void 0;
|
|
339
|
+
this.markTerminalAssistantCandidateSupersededBy(rawItemId);
|
|
340
|
+
}
|
|
341
|
+
if (role !== "assistant") return;
|
|
342
|
+
if (phase === "commentary" && this.pendingRawCommentaryEchoes > 0) {
|
|
343
|
+
this.pendingRawCommentaryEchoes -= 1;
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
const text = extractRawAssistantText(item);
|
|
347
|
+
if (isPendingTerminalAssistantEcho) {
|
|
348
|
+
const typedItemId = pendingTerminalAssistantEchoItemId;
|
|
349
|
+
this.pendingRawTerminalAssistantEchoItemId = void 0;
|
|
350
|
+
if (this.assistantTextByItem.get(typedItemId)?.trim() || !text) return;
|
|
351
|
+
this.rememberAssistantItem(typedItemId);
|
|
352
|
+
this.assistantTextByItem.set(typedItemId, text);
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
if (!text) return;
|
|
356
|
+
const itemId = rawItemId ?? `raw-assistant-${this.assistantItemOrder.length + 1}`;
|
|
357
|
+
const isIdlessTerminalAssistantAfterCompletedWork = candidateWasSupersededBeforeRaw && rawItemId === void 0 && pendingTerminalAssistantEchoItemId === void 0 && activeItemIds.size === 0;
|
|
358
|
+
if (phase !== "commentary" && candidateWasSupersededBeforeRaw && itemId !== this.streamedPartialAssistantItemId && !isIdlessTerminalAssistantAfterCompletedWork) return;
|
|
359
|
+
if (phase) this.assistantPhaseByItem.set(itemId, phase);
|
|
360
|
+
this.rememberAssistantItem(itemId);
|
|
361
|
+
this.assistantTextByItem.set(itemId, text);
|
|
362
|
+
this.rawPromotedAssistantItemIds.add(itemId);
|
|
363
|
+
if (phase === "commentary") this.emitCommentaryProgress({
|
|
364
|
+
itemId,
|
|
365
|
+
text
|
|
366
|
+
});
|
|
367
|
+
else this.markLatestTerminalAssistantCandidate(itemId, activeItemIds, { canReleaseAfterToolHandoff: isIdlessTerminalAssistantAfterCompletedWork });
|
|
368
|
+
}
|
|
369
|
+
collectAssistantTexts() {
|
|
370
|
+
const finalText = this.resolveFinalAssistantTextItem()?.text;
|
|
371
|
+
return finalText ? [finalText] : [];
|
|
372
|
+
}
|
|
373
|
+
collectCommentaryMessages() {
|
|
374
|
+
return this.assistantItemOrder.flatMap((itemId) => {
|
|
375
|
+
if (!this.isCommentaryAssistantItem(itemId)) return [];
|
|
376
|
+
const text = this.assistantTextByItem.get(itemId)?.trim();
|
|
377
|
+
const timestamp = this.assistantTimestampByItem.get(itemId);
|
|
378
|
+
if (!text || timestamp === void 0) return [];
|
|
379
|
+
return [{
|
|
380
|
+
itemId,
|
|
381
|
+
message: createAssistantCommentaryMessage(this.params, text, itemId, timestamp)
|
|
382
|
+
}];
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
finalizeAnswerCandidate(turn) {
|
|
386
|
+
if (turn.status !== "completed") {
|
|
387
|
+
this.supersedeVisibleAnswerCandidate();
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
const turnItems = turn.items ?? [];
|
|
391
|
+
const authoritativeIndex = turnItems.findLastIndex((item) => item.type === "agentMessage" && readItemString(item, "phase") === "final_answer" && typeof item.text === "string" && item.text.trim().length > 0);
|
|
392
|
+
const authoritative = authoritativeIndex >= 0 ? turnItems[authoritativeIndex] : void 0;
|
|
393
|
+
if (turnItems.slice(authoritativeIndex + 1).some(shouldClearTerminalPresentationForNativeItem) || authoritative?.id === this.latestTerminalAssistantCandidateItemId && this.latestTerminalAssistantCandidateSuperseded) {
|
|
394
|
+
this.supersedeVisibleAnswerCandidate();
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
const itemId = authoritative?.id ?? this.visibleAnswerCandidateItemId;
|
|
398
|
+
if (!itemId) return;
|
|
399
|
+
if (itemId !== this.visibleAnswerCandidateItemId) {
|
|
400
|
+
this.supersedeVisibleAnswerCandidate();
|
|
401
|
+
this.visibleAnswerCandidateItemId = itemId;
|
|
402
|
+
}
|
|
403
|
+
this.emitAnswerCandidate(itemId, "selected");
|
|
404
|
+
}
|
|
405
|
+
hasAssistantItemTextForSynthesis() {
|
|
406
|
+
for (let i = this.assistantItemOrder.length - 1; i >= 0; i -= 1) {
|
|
407
|
+
const itemId = this.assistantItemOrder[i];
|
|
408
|
+
if (!itemId || this.assistantPhaseByItem.get(itemId) === "commentary") continue;
|
|
409
|
+
const text = this.assistantTextByItem.get(itemId);
|
|
410
|
+
if (text && text.length > 0) return true;
|
|
411
|
+
}
|
|
412
|
+
return false;
|
|
413
|
+
}
|
|
414
|
+
createCurrentAttemptAssistantMessage(options) {
|
|
415
|
+
for (let i = this.assistantItemOrder.length - 1; i >= 0; i -= 1) {
|
|
416
|
+
const itemId = this.assistantItemOrder[i];
|
|
417
|
+
if (!itemId || this.isCommentaryAssistantItem(itemId) || !this.assistantTextByItem.has(itemId)) continue;
|
|
418
|
+
const text = this.assistantTextByItem.get(itemId) ?? "";
|
|
419
|
+
const normalizedText = text.trim();
|
|
420
|
+
if (normalizedText && this.isToolProgressEchoText(itemId, normalizedText)) continue;
|
|
421
|
+
return this.createAssistantMessage(text, options);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
createAssistantMessage(text, options) {
|
|
425
|
+
return createAssistantMessage(this.params, text, options);
|
|
426
|
+
}
|
|
427
|
+
createAssistantMirrorMessage(title, text) {
|
|
428
|
+
return createAssistantMirrorMessage(this.params, title, text);
|
|
429
|
+
}
|
|
430
|
+
rememberAssistantPhase(item) {
|
|
431
|
+
if (item?.type !== "agentMessage") return;
|
|
432
|
+
const phase = readItemString(item, "phase");
|
|
433
|
+
if (phase) this.assistantPhaseByItem.set(item.id, phase);
|
|
434
|
+
}
|
|
435
|
+
isCommentaryAssistantItem(itemId) {
|
|
436
|
+
return this.assistantPhaseByItem.get(itemId) === "commentary";
|
|
437
|
+
}
|
|
438
|
+
isFinalAnswerAssistantItem(itemId) {
|
|
439
|
+
return this.assistantPhaseByItem.get(itemId) === "final_answer";
|
|
440
|
+
}
|
|
441
|
+
shouldStreamAssistantPartial(itemId) {
|
|
442
|
+
return this.assistantPhaseByItem.get(itemId) === "final_answer";
|
|
443
|
+
}
|
|
444
|
+
emitCommentaryProgress(params) {
|
|
445
|
+
const progressText = params.text.replace(/\s+/g, " ").trim();
|
|
446
|
+
if (!progressText || this.lastCommentaryProgressTextByItem.get(params.itemId) === progressText) return;
|
|
447
|
+
this.lastCommentaryProgressTextByItem.set(params.itemId, progressText);
|
|
448
|
+
this.emitAgentEvent({
|
|
449
|
+
stream: "item",
|
|
450
|
+
data: {
|
|
451
|
+
itemId: params.itemId,
|
|
452
|
+
kind: "preamble",
|
|
453
|
+
title: "Preamble",
|
|
454
|
+
phase: "update",
|
|
455
|
+
progressText,
|
|
456
|
+
source: "codex-app-server"
|
|
457
|
+
}
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
emitAnswerCandidate(itemId, status) {
|
|
461
|
+
const text = this.assistantTextByItem.get(itemId)?.trim();
|
|
462
|
+
if (!text) return;
|
|
463
|
+
if (status === "candidate" && this.visibleAnswerCandidateItemId !== itemId) {
|
|
464
|
+
this.supersedeVisibleAnswerCandidate();
|
|
465
|
+
this.visibleAnswerCandidateItemId = itemId;
|
|
466
|
+
}
|
|
467
|
+
const signature = `${status}\0${text}`;
|
|
468
|
+
if (this.lastAnswerCandidateEventByItem.get(itemId) === signature) return;
|
|
469
|
+
this.lastAnswerCandidateEventByItem.set(itemId, signature);
|
|
470
|
+
this.emitAgentEvent({
|
|
471
|
+
stream: "item",
|
|
472
|
+
data: {
|
|
473
|
+
itemId,
|
|
474
|
+
kind: "answer_candidate",
|
|
475
|
+
title: "Answer candidate",
|
|
476
|
+
phase: "update",
|
|
477
|
+
status,
|
|
478
|
+
progressText: text,
|
|
479
|
+
source: "codex-app-server",
|
|
480
|
+
hideFromChannelProgress: true
|
|
481
|
+
}
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
supersedeVisibleAnswerCandidate() {
|
|
485
|
+
const itemId = this.visibleAnswerCandidateItemId;
|
|
486
|
+
if (!itemId) return;
|
|
487
|
+
this.emitAnswerCandidate(itemId, "superseded");
|
|
488
|
+
this.visibleAnswerCandidateItemId = void 0;
|
|
489
|
+
}
|
|
490
|
+
markLatestTerminalAssistantCandidate(itemId, activeItemIds, options) {
|
|
491
|
+
this.latestTerminalAssistantCandidateItemId = itemId;
|
|
492
|
+
this.latestTerminalAssistantCandidateSuperseded = false;
|
|
493
|
+
this.latestTerminalAssistantCandidateCanReleaseAfterToolHandoff = options?.canReleaseAfterToolHandoff === true;
|
|
494
|
+
this.terminalAssistantCandidateEarlierActiveItemIds = new Set(activeItemIds);
|
|
495
|
+
}
|
|
496
|
+
markTerminalAssistantCandidateSupersededBy(itemId, options) {
|
|
497
|
+
if (!this.latestTerminalAssistantCandidateItemId) return;
|
|
498
|
+
if (itemId && this.terminalAssistantCandidateEarlierActiveItemIds.has(itemId)) {
|
|
499
|
+
if (!options?.preserveEarlierActiveItem) this.terminalAssistantCandidateEarlierActiveItemIds.delete(itemId);
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
this.latestTerminalAssistantCandidateSuperseded = true;
|
|
503
|
+
this.latestTerminalAssistantCandidateCanReleaseAfterToolHandoff = false;
|
|
504
|
+
this.terminalAssistantCandidateEarlierActiveItemIds.clear();
|
|
505
|
+
this.supersedeVisibleAnswerCandidate();
|
|
506
|
+
}
|
|
507
|
+
resolveFinalAssistantTextItem() {
|
|
508
|
+
for (let i = this.assistantItemOrder.length - 1; i >= 0; i -= 1) {
|
|
509
|
+
const itemId = this.assistantItemOrder[i];
|
|
510
|
+
if (!itemId) continue;
|
|
511
|
+
const text = this.assistantTextByItem.get(itemId)?.trim();
|
|
512
|
+
if (this.assistantPhaseByItem.get(itemId) === "commentary") continue;
|
|
513
|
+
if (text && !this.isToolProgressEchoText(itemId, text)) return {
|
|
514
|
+
itemId,
|
|
515
|
+
text
|
|
516
|
+
};
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
rememberAssistantItem(itemId) {
|
|
520
|
+
if (!itemId || this.assistantItemOrder.includes(itemId)) return;
|
|
521
|
+
this.assistantItemOrder.push(itemId);
|
|
522
|
+
this.assistantTimestampByItem.set(itemId, this.nextTranscriptTimestamp());
|
|
523
|
+
}
|
|
524
|
+
isToolProgressEchoText(itemId, text) {
|
|
525
|
+
return this.rawPromotedAssistantItemIds.has(itemId) && this.matchesToolProgressEcho(text);
|
|
526
|
+
}
|
|
527
|
+
};
|
|
528
|
+
//#endregion
|
|
529
|
+
//#region extensions/codex/src/app-server/event-projector-diagnostics.ts
|
|
530
|
+
function redactCodexEventKind(method) {
|
|
531
|
+
return redactSensitiveText(sanitizeTerminalText(method));
|
|
532
|
+
}
|
|
533
|
+
var CodexProjectionDiagnostics = class {
|
|
534
|
+
constructor(threadId, turnId) {
|
|
535
|
+
this.threadId = threadId;
|
|
536
|
+
this.turnId = turnId;
|
|
537
|
+
this.warningKeys = /* @__PURE__ */ new Set();
|
|
538
|
+
}
|
|
539
|
+
warnUnknownItemStatus(item) {
|
|
540
|
+
if (!item) return;
|
|
541
|
+
const status = unknownItemStatus(item);
|
|
542
|
+
if (!status) return;
|
|
543
|
+
const safeStatus = redactCodexEventKind(status);
|
|
544
|
+
const safeItemType = redactCodexEventKind(item.type);
|
|
545
|
+
this.warnOnce(JSON.stringify([
|
|
546
|
+
"status",
|
|
547
|
+
item.type,
|
|
548
|
+
status
|
|
549
|
+
]), "codex app-server item reported unknown status; continuing projection", {
|
|
550
|
+
itemId: item.id,
|
|
551
|
+
itemType: safeItemType,
|
|
552
|
+
status: safeStatus
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
warnUnknownEvent(notification, params) {
|
|
556
|
+
const notificationThreadId = readCodexNotificationThreadId(params);
|
|
557
|
+
const notificationTurnId = readCodexNotificationTurnId(params);
|
|
558
|
+
const eventKind = redactCodexEventKind(notification.method);
|
|
559
|
+
this.warnOnce(JSON.stringify(["method", notification.method]), `codex app-server projector received unknown event kind; continuing: ${eventKind}`, {
|
|
560
|
+
eventKind,
|
|
561
|
+
activeThreadId: this.threadId,
|
|
562
|
+
activeTurnId: this.turnId,
|
|
563
|
+
threadId: notificationThreadId,
|
|
564
|
+
turnId: notificationTurnId,
|
|
565
|
+
matchesActiveThread: notificationThreadId === this.threadId,
|
|
566
|
+
matchesActiveTurn: notificationTurnId === this.turnId
|
|
567
|
+
});
|
|
568
|
+
}
|
|
569
|
+
warnOnce(key, message, context) {
|
|
570
|
+
if (this.warningKeys.has(key)) return;
|
|
571
|
+
this.warningKeys.add(key);
|
|
572
|
+
embeddedAgentLog.warn(message, context);
|
|
573
|
+
}
|
|
574
|
+
};
|
|
575
|
+
//#endregion
|
|
576
|
+
//#region extensions/codex/src/app-server/event-projector-tool-output.ts
|
|
577
|
+
const TOOL_TRANSCRIPT_OUTPUT_MAX_CHARS = 1e4;
|
|
578
|
+
const TOOL_OUTPUT_TRUNCATION_NOTICE_PREFIX = "...(OpenClaw truncated Codex native tool output";
|
|
579
|
+
var ToolOutputAccumulator = class {
|
|
580
|
+
constructor() {
|
|
581
|
+
this.prefixByItem = /* @__PURE__ */ new Map();
|
|
582
|
+
this.originalLengthByItem = /* @__PURE__ */ new Map();
|
|
583
|
+
this.normalizedLengthByItem = /* @__PURE__ */ new Map();
|
|
584
|
+
this.trimStateByItem = /* @__PURE__ */ new Map();
|
|
585
|
+
this.truncatedItemIds = /* @__PURE__ */ new Set();
|
|
586
|
+
this.textByItem = /* @__PURE__ */ new Map();
|
|
587
|
+
}
|
|
588
|
+
append(itemId, delta) {
|
|
589
|
+
const originalLength = (this.originalLengthByItem.get(itemId) ?? this.textByItem.get(itemId)?.length ?? 0) + delta.length;
|
|
590
|
+
this.originalLengthByItem.set(itemId, originalLength);
|
|
591
|
+
const normalizedLength = updateToolOutputTrimState(this.trimStateByItem, itemId, delta);
|
|
592
|
+
this.normalizedLengthByItem.set(itemId, normalizedLength);
|
|
593
|
+
if (this.truncatedItemIds.has(itemId)) {
|
|
594
|
+
const next = appendBoundedToolTranscriptText(this.prefixByItem.get(itemId) ?? this.textByItem.get(itemId) ?? "", "", originalLength);
|
|
595
|
+
this.prefixByItem.set(itemId, next.rawPrefix);
|
|
596
|
+
this.textByItem.set(itemId, next.text);
|
|
597
|
+
return {
|
|
598
|
+
text: next.text,
|
|
599
|
+
originalLength,
|
|
600
|
+
normalizedLength,
|
|
601
|
+
rawPrefix: next.rawPrefix
|
|
602
|
+
};
|
|
603
|
+
}
|
|
604
|
+
const next = appendBoundedToolTranscriptText(this.prefixByItem.get(itemId) ?? this.textByItem.get(itemId) ?? "", delta, originalLength);
|
|
605
|
+
this.prefixByItem.set(itemId, next.rawPrefix);
|
|
606
|
+
this.textByItem.set(itemId, next.text);
|
|
607
|
+
if (originalLength > 1e4) this.truncatedItemIds.add(itemId);
|
|
608
|
+
return {
|
|
609
|
+
text: next.text,
|
|
610
|
+
originalLength,
|
|
611
|
+
normalizedLength,
|
|
612
|
+
rawPrefix: next.rawPrefix
|
|
613
|
+
};
|
|
614
|
+
}
|
|
615
|
+
};
|
|
616
|
+
function updateToolOutputTrimState(trimStateByItem, itemId, delta) {
|
|
617
|
+
const state = trimStateByItem.get(itemId) ?? {
|
|
618
|
+
totalLength: 0,
|
|
619
|
+
leadingWhitespaceLength: 0,
|
|
620
|
+
trailingWhitespaceLength: 0,
|
|
621
|
+
sawNonWhitespace: false
|
|
622
|
+
};
|
|
623
|
+
state.totalLength += delta.length;
|
|
624
|
+
const firstNonWhitespace = delta.search(/\S/u);
|
|
625
|
+
if (firstNonWhitespace === -1) {
|
|
626
|
+
if (!state.sawNonWhitespace) state.leadingWhitespaceLength += delta.length;
|
|
627
|
+
state.trailingWhitespaceLength += delta.length;
|
|
628
|
+
trimStateByItem.set(itemId, state);
|
|
629
|
+
return state.sawNonWhitespace ? state.totalLength - state.leadingWhitespaceLength - state.trailingWhitespaceLength : 0;
|
|
630
|
+
}
|
|
631
|
+
if (!state.sawNonWhitespace) {
|
|
632
|
+
state.leadingWhitespaceLength += firstNonWhitespace;
|
|
633
|
+
state.sawNonWhitespace = true;
|
|
634
|
+
}
|
|
635
|
+
state.trailingWhitespaceLength = delta.match(/\s*$/u)?.[0].length ?? 0;
|
|
636
|
+
trimStateByItem.set(itemId, state);
|
|
637
|
+
return state.totalLength - state.leadingWhitespaceLength - state.trailingWhitespaceLength;
|
|
638
|
+
}
|
|
639
|
+
function toolOutputRawEchoSignature(text) {
|
|
640
|
+
const trimmed = text.trim();
|
|
641
|
+
if (!trimmed) return;
|
|
642
|
+
return {
|
|
643
|
+
rawLength: trimmed.length,
|
|
644
|
+
rawPrefix: trimmed.slice(0, TOOL_TRANSCRIPT_OUTPUT_MAX_CHARS)
|
|
645
|
+
};
|
|
646
|
+
}
|
|
647
|
+
function normalizeToolTranscriptArguments(value) {
|
|
648
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
|
|
649
|
+
return value;
|
|
650
|
+
}
|
|
651
|
+
function collectDynamicToolContentText(contentItems) {
|
|
652
|
+
if (!Array.isArray(contentItems)) return "";
|
|
653
|
+
return contentItems.flatMap((entry) => {
|
|
654
|
+
if (!isJsonObject(entry)) return [];
|
|
655
|
+
const text = readString$5(entry, "text");
|
|
656
|
+
return text ? [text] : [];
|
|
657
|
+
}).join("\n");
|
|
658
|
+
}
|
|
659
|
+
function appendBoundedToolTranscriptText(currentPrefix, delta, originalLength) {
|
|
660
|
+
if (originalLength <= 1e4) {
|
|
661
|
+
const rawPrefix = currentPrefix + delta;
|
|
662
|
+
return {
|
|
663
|
+
text: rawPrefix,
|
|
664
|
+
rawPrefix
|
|
665
|
+
};
|
|
666
|
+
}
|
|
667
|
+
const notice = toolTranscriptTruncationNotice(originalLength);
|
|
668
|
+
if (notice.length >= 1e4) return {
|
|
669
|
+
text: notice.slice(0, TOOL_TRANSCRIPT_OUTPUT_MAX_CHARS),
|
|
670
|
+
rawPrefix: ""
|
|
671
|
+
};
|
|
672
|
+
const textBudget = TOOL_TRANSCRIPT_OUTPUT_MAX_CHARS - notice.length;
|
|
673
|
+
const remaining = Math.max(0, textBudget - currentPrefix.length);
|
|
674
|
+
const rawPrefix = truncateUtf16Safe(remaining > 0 ? `${currentPrefix}${truncateUtf16Safe(delta, remaining)}` : currentPrefix, textBudget);
|
|
675
|
+
return {
|
|
676
|
+
text: `${rawPrefix}${notice}`,
|
|
677
|
+
rawPrefix
|
|
678
|
+
};
|
|
679
|
+
}
|
|
680
|
+
function toolTranscriptTruncationNotice(originalLength) {
|
|
681
|
+
return `\n${`${TOOL_OUTPUT_TRUNCATION_NOTICE_PREFIX}: original ${originalLength} chars, showing ${TOOL_TRANSCRIPT_OUTPUT_MAX_CHARS}; rerun with narrower args.)`}`;
|
|
682
|
+
}
|
|
683
|
+
function truncateToolTranscriptText(text, originalLength = text.length) {
|
|
684
|
+
if (originalLength <= 1e4 && text.length <= 1e4) return text;
|
|
685
|
+
const notice = toolTranscriptTruncationNotice(originalLength);
|
|
686
|
+
if (notice.length >= 1e4) return notice.slice(1, 10001);
|
|
687
|
+
return `${truncateUtf16Safe(text, TOOL_TRANSCRIPT_OUTPUT_MAX_CHARS - notice.length)}${notice}`;
|
|
688
|
+
}
|
|
689
|
+
function formatToolSummary(toolName, meta) {
|
|
690
|
+
const trimmedMeta = meta?.trim();
|
|
691
|
+
return formatToolAggregate(toolName, trimmedMeta ? [trimmedMeta] : void 0, { markdown: true });
|
|
692
|
+
}
|
|
693
|
+
function formatToolOutput(toolName, meta, output) {
|
|
694
|
+
const formattedOutput = formatToolProgressOutput(output);
|
|
695
|
+
if (!formattedOutput) return formatToolSummary(toolName, meta);
|
|
696
|
+
const fence = markdownFenceForText(formattedOutput);
|
|
697
|
+
return `${formatToolSummary(toolName, meta)}\n${fence}txt\n${formattedOutput}\n${fence}`;
|
|
698
|
+
}
|
|
699
|
+
function markdownFenceForText(text) {
|
|
700
|
+
return "`".repeat(Math.max(3, longestBacktickRun(text) + 1));
|
|
701
|
+
}
|
|
702
|
+
function longestBacktickRun(value) {
|
|
703
|
+
let longest = 0;
|
|
704
|
+
let current = 0;
|
|
705
|
+
for (const char of value) {
|
|
706
|
+
if (char === "`") {
|
|
707
|
+
current += 1;
|
|
708
|
+
longest = Math.max(longest, current);
|
|
709
|
+
continue;
|
|
710
|
+
}
|
|
711
|
+
current = 0;
|
|
712
|
+
}
|
|
713
|
+
return longest;
|
|
714
|
+
}
|
|
715
|
+
//#endregion
|
|
716
|
+
//#region extensions/codex/src/app-server/tool-progress-normalization.ts
|
|
717
|
+
/**
|
|
718
|
+
* Normalizes and sanitizes Codex dynamic-tool progress payloads before they are
|
|
719
|
+
* emitted into OpenClaw events or logs.
|
|
720
|
+
*/
|
|
721
|
+
/** Maps OpenClaw tool-progress config to the mode used by Codex progress metadata. */
|
|
722
|
+
function resolveCodexToolProgressDetailMode(value) {
|
|
723
|
+
return value === "raw" ? "raw" : "explain";
|
|
724
|
+
}
|
|
725
|
+
/** Recursively redacts sensitive strings and handles circular values in event payloads. */
|
|
726
|
+
function sanitizeCodexAgentEventValue(value, seen = /* @__PURE__ */ new WeakSet()) {
|
|
727
|
+
if (typeof value === "string") return redactToolPayloadText(value);
|
|
728
|
+
if (Array.isArray(value)) {
|
|
729
|
+
if (seen.has(value)) return "[Circular]";
|
|
730
|
+
seen.add(value);
|
|
731
|
+
return value.map((entry) => sanitizeCodexAgentEventValue(entry, seen));
|
|
732
|
+
}
|
|
733
|
+
if (value && typeof value === "object") {
|
|
734
|
+
if (seen.has(value)) return "[Circular]";
|
|
735
|
+
seen.add(value);
|
|
736
|
+
const out = {};
|
|
737
|
+
for (const [key, child] of Object.entries(value)) out[key] = typeof child === "string" ? redactSensitiveFieldValue(key, child) : sanitizeCodexAgentEventValue(child, seen);
|
|
738
|
+
return out;
|
|
739
|
+
}
|
|
740
|
+
return value;
|
|
741
|
+
}
|
|
742
|
+
/** Sanitizes a record-shaped Codex agent event payload. */
|
|
743
|
+
function sanitizeCodexAgentEventRecord(value) {
|
|
744
|
+
return sanitizeCodexAgentEventValue(value);
|
|
745
|
+
}
|
|
746
|
+
/** Sanitizes dynamic-tool arguments before diagnostic/event emission. */
|
|
747
|
+
function sanitizeCodexToolArguments(value) {
|
|
748
|
+
if (!isJsonObject(value)) return;
|
|
749
|
+
return sanitizeCodexAgentEventRecord(value);
|
|
750
|
+
}
|
|
751
|
+
/** Sanitizes a Codex dynamic-tool response before diagnostic/event emission. */
|
|
752
|
+
function sanitizeCodexToolResponse(response) {
|
|
753
|
+
return sanitizeCodexAgentEventRecord(response);
|
|
754
|
+
}
|
|
755
|
+
/** Infers compact human-readable tool metadata from Codex dynamic-tool arguments. */
|
|
756
|
+
function inferCodexDynamicToolMeta(call, detailMode) {
|
|
757
|
+
return inferToolMetaFromArgs(call.tool, call.arguments, { detailMode });
|
|
758
|
+
}
|
|
759
|
+
//#endregion
|
|
760
|
+
//#region extensions/codex/src/app-server/event-projector-tool-items.ts
|
|
761
|
+
function nativeToolActionFingerprint(item) {
|
|
762
|
+
if (item.type === "commandExecution" && typeof item.command === "string") return JSON.stringify({
|
|
763
|
+
type: item.type,
|
|
764
|
+
command: item.command,
|
|
765
|
+
cwd: typeof item.cwd === "string" ? item.cwd : ""
|
|
766
|
+
});
|
|
767
|
+
if (item.type === "fileChange") return JSON.stringify({
|
|
768
|
+
type: item.type,
|
|
769
|
+
changes: itemFileChanges(item)
|
|
770
|
+
});
|
|
771
|
+
}
|
|
772
|
+
function isNativePostToolUseRelayItem(item) {
|
|
773
|
+
switch (item.type) {
|
|
774
|
+
case "commandExecution":
|
|
775
|
+
case "fileChange":
|
|
776
|
+
case "mcpToolCall": return true;
|
|
777
|
+
default: return false;
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
function shouldSuppressChannelProgressForItem(item) {
|
|
781
|
+
if (shouldSynthesizeToolProgressForItem(item)) return true;
|
|
782
|
+
return item.type === "dynamicToolCall";
|
|
783
|
+
}
|
|
784
|
+
function itemToolArgs(item) {
|
|
785
|
+
if (item.type === "commandExecution") return sanitizeCodexAgentEventRecord({
|
|
786
|
+
command: item.command,
|
|
787
|
+
...typeof item.cwd === "string" ? { cwd: item.cwd } : {}
|
|
788
|
+
});
|
|
789
|
+
if (item.type === "fileChange") return sanitizeCodexAgentEventRecord({ changes: itemFileChangesForTranscript(item) });
|
|
790
|
+
if (item.type === "webSearch") return webSearchToolArgs(item);
|
|
791
|
+
if (item.type === "dynamicToolCall" || item.type === "mcpToolCall") return sanitizeCodexToolArguments(item.arguments);
|
|
792
|
+
}
|
|
793
|
+
function webSearchToolArgs(item) {
|
|
794
|
+
const action = isJsonObject(item.action) ? item.action : void 0;
|
|
795
|
+
const actionType = action ? readNonEmptyString$1(action, "type") : void 0;
|
|
796
|
+
const queries = action && actionType === "search" ? readNonEmptyStringArray(action, "queries") : [];
|
|
797
|
+
const query = normalizeNonEmptyString(item.query) ?? (action && actionType === "search" ? readNonEmptyString$1(action, "query") : void 0) ?? queries[0];
|
|
798
|
+
const url = action ? readNonEmptyString$1(action, "url") : void 0;
|
|
799
|
+
const pattern = action ? readNonEmptyString$1(action, "pattern") : void 0;
|
|
800
|
+
const args = {};
|
|
801
|
+
if (query) args.query = query;
|
|
802
|
+
if (queries.length > 0) args.queries = queries;
|
|
803
|
+
if (actionType && actionType !== "search") args.action = actionType;
|
|
804
|
+
if (url) args.url = url;
|
|
805
|
+
if (pattern) args.pattern = pattern;
|
|
806
|
+
if (!query && !url && !pattern) args.queryUnavailable = true;
|
|
807
|
+
return sanitizeCodexAgentEventRecord(args);
|
|
808
|
+
}
|
|
809
|
+
function itemToolResult(item) {
|
|
810
|
+
if (item.type === "commandExecution") return { result: sanitizeCodexAgentEventRecord({
|
|
811
|
+
status: item.status,
|
|
812
|
+
exitCode: item.exitCode,
|
|
813
|
+
durationMs: item.durationMs
|
|
814
|
+
}) };
|
|
815
|
+
if (item.type === "fileChange") return { result: sanitizeCodexAgentEventRecord({
|
|
816
|
+
status: item.status,
|
|
817
|
+
changes: itemFileChanges(item)
|
|
818
|
+
}) };
|
|
819
|
+
if (item.type === "mcpToolCall") return { result: sanitizeCodexAgentEventRecord({
|
|
820
|
+
status: item.status,
|
|
821
|
+
durationMs: item.durationMs,
|
|
822
|
+
...item.error ? { error: item.error } : {},
|
|
823
|
+
...item.result ? { result: item.result } : {}
|
|
824
|
+
}) };
|
|
825
|
+
if (item.type === "webSearch") return { result: webSearchToolResult(item) };
|
|
826
|
+
return {};
|
|
827
|
+
}
|
|
828
|
+
function webSearchToolResult(item) {
|
|
829
|
+
return sanitizeCodexAgentEventRecord({
|
|
830
|
+
status: itemStatus(item),
|
|
831
|
+
...typeof item.durationMs === "number" ? { durationMs: item.durationMs } : {},
|
|
832
|
+
...webSearchToolArgs(item)
|
|
833
|
+
});
|
|
834
|
+
}
|
|
835
|
+
function itemFileChangeRecords(item) {
|
|
836
|
+
const changes = item.changes;
|
|
837
|
+
return Array.isArray(changes) ? changes.filter(isJsonObject) : [];
|
|
838
|
+
}
|
|
839
|
+
function itemFileChanges(item) {
|
|
840
|
+
return itemFileChangeRecords(item).flatMap((change) => {
|
|
841
|
+
const path = normalizeNonEmptyString(change.path);
|
|
842
|
+
if (!path || change.kind === void 0) return [];
|
|
843
|
+
return [{
|
|
844
|
+
path,
|
|
845
|
+
kind: change.kind
|
|
846
|
+
}];
|
|
847
|
+
});
|
|
848
|
+
}
|
|
849
|
+
function fileChangeKindType(kind) {
|
|
850
|
+
if (typeof kind === "string") return kind;
|
|
851
|
+
return isJsonObject(kind) ? normalizeNonEmptyString(kind.type) : void 0;
|
|
852
|
+
}
|
|
853
|
+
function countFileContentLines(content) {
|
|
854
|
+
if (!content) return 0;
|
|
855
|
+
const lines = content.replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n");
|
|
856
|
+
if (lines.length > 1 && lines.at(-1) === "") lines.pop();
|
|
857
|
+
return lines.length;
|
|
858
|
+
}
|
|
859
|
+
function fileChangeDiffStat(diff, kind) {
|
|
860
|
+
const kindType = fileChangeKindType(kind);
|
|
861
|
+
if (kindType === "add") return {
|
|
862
|
+
added: countFileContentLines(diff),
|
|
863
|
+
removed: 0
|
|
864
|
+
};
|
|
865
|
+
if (kindType === "delete") return {
|
|
866
|
+
added: 0,
|
|
867
|
+
removed: countFileContentLines(diff)
|
|
868
|
+
};
|
|
869
|
+
let added = 0;
|
|
870
|
+
let removed = 0;
|
|
871
|
+
let inHunk = false;
|
|
872
|
+
for (const line of diff.replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n")) {
|
|
873
|
+
if (line.startsWith("@@")) {
|
|
874
|
+
inHunk = true;
|
|
875
|
+
continue;
|
|
876
|
+
}
|
|
877
|
+
if (!inHunk) continue;
|
|
878
|
+
if (line.startsWith("+")) added += 1;
|
|
879
|
+
else if (line.startsWith("-")) removed += 1;
|
|
880
|
+
}
|
|
881
|
+
return {
|
|
882
|
+
added,
|
|
883
|
+
removed
|
|
884
|
+
};
|
|
885
|
+
}
|
|
886
|
+
function truncateFileChangeDiffAtLineBoundary(diff, maxChars) {
|
|
887
|
+
if (diff.length <= maxChars) return { diff };
|
|
888
|
+
if (maxChars <= 0) return { diffTruncated: true };
|
|
889
|
+
const boundary = diff.lastIndexOf("\n", maxChars - 1);
|
|
890
|
+
return boundary >= 0 ? {
|
|
891
|
+
diff: diff.slice(0, boundary + 1),
|
|
892
|
+
diffTruncated: true
|
|
893
|
+
} : { diffTruncated: true };
|
|
894
|
+
}
|
|
895
|
+
function itemFileChangesForTranscript(item) {
|
|
896
|
+
let remainingDiffChars = 1e4;
|
|
897
|
+
return itemFileChangeRecords(item).flatMap((change) => {
|
|
898
|
+
const path = normalizeNonEmptyString(change.path);
|
|
899
|
+
if (!path || change.kind === void 0) return [];
|
|
900
|
+
const result = {
|
|
901
|
+
path,
|
|
902
|
+
kind: change.kind
|
|
903
|
+
};
|
|
904
|
+
if (typeof change.diff !== "string") return [result];
|
|
905
|
+
result.stat = fileChangeDiffStat(change.diff, change.kind);
|
|
906
|
+
const bounded = truncateFileChangeDiffAtLineBoundary(change.diff, remainingDiffChars);
|
|
907
|
+
if (bounded.diff !== void 0) {
|
|
908
|
+
result.diff = bounded.diff;
|
|
909
|
+
remainingDiffChars -= bounded.diff.length;
|
|
910
|
+
}
|
|
911
|
+
if (bounded.diffTruncated) result.diffTruncated = true;
|
|
912
|
+
return [result];
|
|
913
|
+
});
|
|
914
|
+
}
|
|
915
|
+
function itemToolError(item, status, outputTextByItem) {
|
|
916
|
+
if (status === "blocked") return "codex native tool blocked";
|
|
917
|
+
if (status !== "failed") return;
|
|
918
|
+
return itemOutputText(item, outputTextByItem) ?? "codex native tool failed";
|
|
919
|
+
}
|
|
920
|
+
function itemMeta(item, detailMode = "explain") {
|
|
921
|
+
if (item.type === "commandExecution" && typeof item.command === "string") return inferToolMetaFromArgs("exec", {
|
|
922
|
+
command: item.command,
|
|
923
|
+
cwd: typeof item.cwd === "string" ? item.cwd : void 0
|
|
924
|
+
}, { detailMode });
|
|
925
|
+
if (item.type === "webSearch") return inferToolMetaFromArgs("web_search", webSearchToolArgs(item), { detailMode });
|
|
926
|
+
const toolName = itemName(item);
|
|
927
|
+
if ((item.type === "dynamicToolCall" || item.type === "mcpToolCall") && toolName) return inferToolMetaFromArgs(toolName, item.arguments, { detailMode });
|
|
928
|
+
}
|
|
929
|
+
function itemOutputText(item, outputTextByItem) {
|
|
930
|
+
if (item.type === "commandExecution") {
|
|
931
|
+
const output = item.aggregatedOutput?.trim() || outputTextByItem?.get(item.id)?.trim();
|
|
932
|
+
return output ? truncateToolTranscriptText(output) : void 0;
|
|
933
|
+
}
|
|
934
|
+
if (item.type === "dynamicToolCall") {
|
|
935
|
+
const output = collectDynamicToolContentText(item.contentItems).trim();
|
|
936
|
+
return output ? truncateToolTranscriptText(output) : void 0;
|
|
937
|
+
}
|
|
938
|
+
if (item.type === "mcpToolCall") {
|
|
939
|
+
const output = item.error ? stringifyJsonValue(item.error) : item.result ? stringifyJsonValue(item.result) : void 0;
|
|
940
|
+
return output ? truncateToolTranscriptText(output) : void 0;
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
function itemTranscriptResultText(item, outputTextByItem) {
|
|
944
|
+
const output = itemOutputText(item, outputTextByItem);
|
|
945
|
+
if (output) return output;
|
|
946
|
+
const result = itemToolResult(item).result;
|
|
947
|
+
const resultText = result ? stringifyJsonValue(result) : void 0;
|
|
948
|
+
return resultText ? truncateToolTranscriptText(resultText) : itemStatus(item);
|
|
949
|
+
}
|
|
950
|
+
function stringifyJsonValue(value) {
|
|
951
|
+
try {
|
|
952
|
+
return JSON.stringify(value, null, 2);
|
|
953
|
+
} catch {
|
|
954
|
+
return;
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
//#endregion
|
|
958
|
+
//#region extensions/codex/src/app-server/event-projector-tool-progress.ts
|
|
959
|
+
const TRANSCRIPT_PROGRESS_SUPPRESSED_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
960
|
+
"message",
|
|
961
|
+
"messages",
|
|
962
|
+
"reply",
|
|
963
|
+
"send",
|
|
964
|
+
"reaction",
|
|
965
|
+
"react",
|
|
966
|
+
"typing"
|
|
967
|
+
]);
|
|
968
|
+
function shouldEmitTranscriptToolProgress(toolName, _args) {
|
|
969
|
+
const normalized = typeof toolName === "string" ? toolName.trim().toLowerCase() : "";
|
|
970
|
+
return Boolean(normalized && !TRANSCRIPT_PROGRESS_SUPPRESSED_TOOL_NAMES.has(normalized));
|
|
971
|
+
}
|
|
972
|
+
var CodexToolProgressProjection = class {
|
|
973
|
+
constructor(params) {
|
|
974
|
+
this.params = params;
|
|
975
|
+
this.echoesByItem = /* @__PURE__ */ new Map();
|
|
976
|
+
this.resultSummaryItemIds = /* @__PURE__ */ new Set();
|
|
977
|
+
this.resultOutputItemIds = /* @__PURE__ */ new Set();
|
|
978
|
+
this.resultOutputStreamedItemIds = /* @__PURE__ */ new Set();
|
|
979
|
+
this.transcriptProgressSuppressedIds = /* @__PURE__ */ new Set();
|
|
980
|
+
this.transcriptArgumentsById = /* @__PURE__ */ new Map();
|
|
981
|
+
this.resultOutputDeltaState = /* @__PURE__ */ new Map();
|
|
982
|
+
this.output = new ToolOutputAccumulator();
|
|
983
|
+
this.metas = /* @__PURE__ */ new Map();
|
|
984
|
+
this.sideEffectingNativeIds = /* @__PURE__ */ new Set();
|
|
985
|
+
this.sideEffectingDynamicIds = /* @__PURE__ */ new Set();
|
|
986
|
+
this.transcriptProgressCallIds = /* @__PURE__ */ new Set();
|
|
987
|
+
}
|
|
988
|
+
get outputTextByItem() {
|
|
989
|
+
return this.output.textByItem;
|
|
990
|
+
}
|
|
991
|
+
get toolMetas() {
|
|
992
|
+
return [...this.metas.values()];
|
|
993
|
+
}
|
|
994
|
+
getToolMeta(itemId) {
|
|
995
|
+
return this.metas.get(itemId);
|
|
996
|
+
}
|
|
997
|
+
get lastToolError() {
|
|
998
|
+
return this.lastNativeToolError;
|
|
999
|
+
}
|
|
1000
|
+
get hasPotentialSideEffects() {
|
|
1001
|
+
return this.sideEffectingNativeIds.size > 0 || this.sideEffectingDynamicIds.size > 0;
|
|
1002
|
+
}
|
|
1003
|
+
setLastToolError(error) {
|
|
1004
|
+
if (!error) {
|
|
1005
|
+
this.lastNativeToolError = void 0;
|
|
1006
|
+
return;
|
|
1007
|
+
}
|
|
1008
|
+
const terminalResolution = this.params.observeToolTerminal?.({
|
|
1009
|
+
toolName: error.toolName,
|
|
1010
|
+
...error.meta ? { meta: error.meta } : {},
|
|
1011
|
+
outcome: "failure",
|
|
1012
|
+
failure: {
|
|
1013
|
+
...error.errorCode ? { errorCode: error.errorCode } : {},
|
|
1014
|
+
...error.error ? { error: error.error } : {},
|
|
1015
|
+
...error.validationErrorSummary ? { validationErrorSummary: error.validationErrorSummary } : {},
|
|
1016
|
+
...error.timedOut ? { timedOut: true } : {},
|
|
1017
|
+
...error.middlewareError ? { middlewareError: true } : {}
|
|
1018
|
+
},
|
|
1019
|
+
nativeMutation: {
|
|
1020
|
+
mutatingAction: error.mutatingAction === true,
|
|
1021
|
+
replaySafe: error.mutatingAction !== true,
|
|
1022
|
+
...error.actionFingerprint ? { actionFingerprint: error.actionFingerprint } : {},
|
|
1023
|
+
...error.fileTarget ? { fileTarget: error.fileTarget } : {}
|
|
1024
|
+
}
|
|
1025
|
+
});
|
|
1026
|
+
this.lastNativeToolError = terminalResolution?.lastToolError ?? (this.lastNativeToolError?.mutatingAction && error.mutatingAction !== true ? this.lastNativeToolError : error);
|
|
1027
|
+
}
|
|
1028
|
+
recordDynamicToolResult(params) {
|
|
1029
|
+
const resultText = collectDynamicToolContentText(params.contentItems);
|
|
1030
|
+
const existing = this.metas.get(params.callId);
|
|
1031
|
+
this.metas.set(params.callId, {
|
|
1032
|
+
toolName: existing?.toolName ?? params.tool,
|
|
1033
|
+
...existing?.meta ? { meta: existing.meta } : {},
|
|
1034
|
+
...params.asyncStarted === true ? { asyncStarted: true } : {},
|
|
1035
|
+
...!params.success ? { isError: true } : {}
|
|
1036
|
+
});
|
|
1037
|
+
if (params.terminalResolution) this.lastNativeToolError = params.terminalResolution.lastToolError;
|
|
1038
|
+
else if (!params.success) this.lastNativeToolError = {
|
|
1039
|
+
toolName: params.tool,
|
|
1040
|
+
error: resultText || (params.terminalType === "blocked" ? "codex dynamic tool blocked" : "codex dynamic tool failed")
|
|
1041
|
+
};
|
|
1042
|
+
else if (this.lastNativeToolError?.mutatingAction !== true) this.lastNativeToolError = void 0;
|
|
1043
|
+
if (params.sideEffectEvidence === true) this.sideEffectingDynamicIds.add(params.callId);
|
|
1044
|
+
}
|
|
1045
|
+
handleOutputDelta(params, toolName) {
|
|
1046
|
+
const itemId = readString$5(params, "itemId");
|
|
1047
|
+
const delta = readString$5(params, "delta");
|
|
1048
|
+
if (!itemId || !delta) return;
|
|
1049
|
+
const storedOutput = this.output.append(itemId, delta);
|
|
1050
|
+
this.rememberEcho(itemId, {
|
|
1051
|
+
displayText: storedOutput.text,
|
|
1052
|
+
rawLength: storedOutput.normalizedLength,
|
|
1053
|
+
rawPrefix: storedOutput.rawPrefix,
|
|
1054
|
+
streamedDisplay: true
|
|
1055
|
+
});
|
|
1056
|
+
if (!this.shouldEmitToolOutput()) return;
|
|
1057
|
+
if (this.transcriptProgressSuppressedIds.has(itemId) || !shouldEmitTranscriptToolProgress(toolName, this.transcriptArgumentsById.get(itemId))) return;
|
|
1058
|
+
const state = this.resultOutputDeltaState.get(itemId) ?? {
|
|
1059
|
+
chars: 0,
|
|
1060
|
+
messages: 0,
|
|
1061
|
+
truncated: false
|
|
1062
|
+
};
|
|
1063
|
+
if (state.truncated) return;
|
|
1064
|
+
const remainingChars = Math.max(0, TOOL_PROGRESS_OUTPUT_MAX_CHARS - state.chars);
|
|
1065
|
+
const remainingMessages = Math.max(0, 20 - state.messages);
|
|
1066
|
+
if (remainingChars === 0 || remainingMessages === 0) {
|
|
1067
|
+
state.truncated = true;
|
|
1068
|
+
this.resultOutputDeltaState.set(itemId, state);
|
|
1069
|
+
this.emitToolResultMessage({
|
|
1070
|
+
itemId,
|
|
1071
|
+
text: formatToolOutput(toolName, void 0, "(output truncated)")
|
|
1072
|
+
});
|
|
1073
|
+
return;
|
|
1074
|
+
}
|
|
1075
|
+
const chunk = delta.length > remainingChars ? truncateUtf16Safe(delta, remainingChars) : delta;
|
|
1076
|
+
state.chars += chunk.length;
|
|
1077
|
+
state.messages += 1;
|
|
1078
|
+
const reachedLimit = delta.length > remainingChars || state.chars >= TOOL_PROGRESS_OUTPUT_MAX_CHARS || state.messages >= 20;
|
|
1079
|
+
if (reachedLimit) state.truncated = true;
|
|
1080
|
+
this.resultOutputDeltaState.set(itemId, state);
|
|
1081
|
+
this.resultOutputStreamedItemIds.add(itemId);
|
|
1082
|
+
this.emitToolResultMessage({
|
|
1083
|
+
itemId,
|
|
1084
|
+
text: formatToolOutput(toolName, void 0, reachedLimit ? `${chunk}\n...(truncated)...` : chunk)
|
|
1085
|
+
});
|
|
1086
|
+
}
|
|
1087
|
+
recordNativeToolError(params) {
|
|
1088
|
+
const executionStarted = params.status !== "blocked";
|
|
1089
|
+
const mutatingAction = executionStarted && isMutatingNativeToolItem(params.item);
|
|
1090
|
+
const actionFingerprint = mutatingAction ? nativeToolActionFingerprint(params.item) : void 0;
|
|
1091
|
+
const isFailure = isNonSuccessItemStatus(params.status);
|
|
1092
|
+
const error = isFailure ? itemToolError(params.item, params.status, this.output.textByItem) : void 0;
|
|
1093
|
+
const terminalResolution = this.params.observeToolTerminal?.({
|
|
1094
|
+
toolCallId: params.item.id,
|
|
1095
|
+
toolName: params.name,
|
|
1096
|
+
arguments: itemToolArgs(params.item),
|
|
1097
|
+
...params.meta ? { meta: params.meta } : {},
|
|
1098
|
+
executionStarted,
|
|
1099
|
+
outcome: isFailure ? "failure" : "success",
|
|
1100
|
+
...isFailure ? { failure: error ? { error } : {} } : {},
|
|
1101
|
+
nativeMutation: {
|
|
1102
|
+
mutatingAction,
|
|
1103
|
+
replaySafe: !mutatingAction,
|
|
1104
|
+
...actionFingerprint ? { actionFingerprint } : {}
|
|
1105
|
+
}
|
|
1106
|
+
});
|
|
1107
|
+
if (terminalResolution) {
|
|
1108
|
+
this.lastNativeToolError = terminalResolution.lastToolError;
|
|
1109
|
+
return;
|
|
1110
|
+
}
|
|
1111
|
+
if (isFailure) this.lastNativeToolError = {
|
|
1112
|
+
toolName: params.name,
|
|
1113
|
+
...params.meta ? { meta: params.meta } : {},
|
|
1114
|
+
...error ? { error } : {},
|
|
1115
|
+
...mutatingAction ? { mutatingAction: true } : {},
|
|
1116
|
+
...actionFingerprint ? { actionFingerprint } : {}
|
|
1117
|
+
};
|
|
1118
|
+
else if (this.lastNativeToolError?.mutatingAction !== true) this.lastNativeToolError = void 0;
|
|
1119
|
+
}
|
|
1120
|
+
emitToolResultSummary(item) {
|
|
1121
|
+
if (!item || !this.params.onToolResult || !this.shouldEmitToolResult()) return;
|
|
1122
|
+
if (this.resultSummaryItemIds.has(item.id)) return;
|
|
1123
|
+
const toolName = itemName(item);
|
|
1124
|
+
if (!toolName || !shouldEmitTranscriptToolProgress(toolName, itemToolArgs(item))) return;
|
|
1125
|
+
this.resultSummaryItemIds.add(item.id);
|
|
1126
|
+
this.emitToolResultMessage({
|
|
1127
|
+
itemId: item.id,
|
|
1128
|
+
text: formatToolSummary(toolName, itemMeta(item, this.toolProgressDetailMode()))
|
|
1129
|
+
});
|
|
1130
|
+
}
|
|
1131
|
+
emitToolResultOutput(item) {
|
|
1132
|
+
if (!item || !this.params.onToolResult || !this.shouldEmitToolOutput()) return;
|
|
1133
|
+
if (this.resultOutputItemIds.has(item.id) || this.resultOutputStreamedItemIds.has(item.id)) return;
|
|
1134
|
+
const toolName = itemName(item);
|
|
1135
|
+
const output = itemOutputText(item, this.output.textByItem);
|
|
1136
|
+
if (!toolName || !output || !shouldEmitTranscriptToolProgress(toolName, itemToolArgs(item))) return;
|
|
1137
|
+
this.emitToolResultMessage({
|
|
1138
|
+
itemId: item.id,
|
|
1139
|
+
text: formatToolOutput(toolName, itemMeta(item, this.toolProgressDetailMode()), output),
|
|
1140
|
+
finalOutput: true,
|
|
1141
|
+
isError: isNonSuccessItemStatus(itemStatus(item))
|
|
1142
|
+
});
|
|
1143
|
+
}
|
|
1144
|
+
recordToolMeta(item) {
|
|
1145
|
+
if (!item) return;
|
|
1146
|
+
if (isSideEffectingNativeToolItem(item)) this.sideEffectingNativeIds.add(item.id);
|
|
1147
|
+
else this.sideEffectingNativeIds.delete(item.id);
|
|
1148
|
+
const toolName = itemName(item);
|
|
1149
|
+
if (!toolName) return;
|
|
1150
|
+
const meta = itemMeta(item, this.toolProgressDetailMode());
|
|
1151
|
+
const status = itemStatus(item);
|
|
1152
|
+
const existing = this.metas.get(item.id);
|
|
1153
|
+
this.metas.set(item.id, {
|
|
1154
|
+
toolName,
|
|
1155
|
+
...meta ? { meta } : {},
|
|
1156
|
+
...existing?.asyncStarted ? { asyncStarted: true } : {},
|
|
1157
|
+
...status !== "running" && isNonSuccessItemStatus(status) ? { isError: true } : {}
|
|
1158
|
+
});
|
|
1159
|
+
}
|
|
1160
|
+
recordTranscriptCall(params) {
|
|
1161
|
+
this.transcriptArgumentsById.set(params.id, params.arguments);
|
|
1162
|
+
if (!shouldEmitTranscriptToolProgress(params.name, params.arguments)) this.transcriptProgressSuppressedIds.add(params.id);
|
|
1163
|
+
else this.transcriptProgressSuppressedIds.delete(params.id);
|
|
1164
|
+
this.emitTranscriptToolCallProgress(params);
|
|
1165
|
+
}
|
|
1166
|
+
recordTranscriptResult(params) {
|
|
1167
|
+
this.emitTranscriptToolResultProgress(params);
|
|
1168
|
+
}
|
|
1169
|
+
matchesEcho(text) {
|
|
1170
|
+
for (const state of this.echoesByItem.values()) {
|
|
1171
|
+
if (state.streamedDisplayText === text || state.displayTexts.includes(text)) return true;
|
|
1172
|
+
if (state.streamedRawSignature && text.length === state.streamedRawSignature.length && text.startsWith(state.streamedRawSignature.prefix)) return true;
|
|
1173
|
+
for (const signature of state.rawSignatures) if (text.length === signature.length && text.startsWith(signature.prefix)) return true;
|
|
1174
|
+
}
|
|
1175
|
+
return false;
|
|
1176
|
+
}
|
|
1177
|
+
rememberCommandAggregateOutputEcho(item) {
|
|
1178
|
+
if (item?.type !== "commandExecution" || typeof item.aggregatedOutput !== "string") return;
|
|
1179
|
+
const signature = toolOutputRawEchoSignature(item.aggregatedOutput);
|
|
1180
|
+
if (signature) this.rememberEcho(item.id, signature);
|
|
1181
|
+
}
|
|
1182
|
+
toolProgressDetailMode() {
|
|
1183
|
+
return resolveCodexToolProgressDetailMode(this.params.toolProgressDetail);
|
|
1184
|
+
}
|
|
1185
|
+
emitToolResultMessage(params) {
|
|
1186
|
+
const rawText = params.text.trim();
|
|
1187
|
+
const text = truncateToolTranscriptText(rawText);
|
|
1188
|
+
if (!text) return;
|
|
1189
|
+
this.rememberEcho(params.itemId, {
|
|
1190
|
+
displayText: text,
|
|
1191
|
+
rawText
|
|
1192
|
+
});
|
|
1193
|
+
if (params.finalOutput) this.resultOutputItemIds.add(params.itemId);
|
|
1194
|
+
try {
|
|
1195
|
+
Promise.resolve(this.params.onToolResult?.({
|
|
1196
|
+
text,
|
|
1197
|
+
...params.isError === true ? { isError: true } : {}
|
|
1198
|
+
})).catch(() => {});
|
|
1199
|
+
} catch {}
|
|
1200
|
+
}
|
|
1201
|
+
shouldEmitToolResult() {
|
|
1202
|
+
return typeof this.params.shouldEmitToolResult === "function" ? this.params.shouldEmitToolResult() : this.params.verboseLevel === "on" || this.params.verboseLevel === "full";
|
|
1203
|
+
}
|
|
1204
|
+
shouldEmitToolOutput() {
|
|
1205
|
+
return typeof this.params.shouldEmitToolOutput === "function" ? this.params.shouldEmitToolOutput() : this.params.verboseLevel === "full";
|
|
1206
|
+
}
|
|
1207
|
+
emitTranscriptToolCallProgress(params) {
|
|
1208
|
+
if (!shouldEmitTranscriptToolProgress(params.name, params.arguments)) return;
|
|
1209
|
+
this.transcriptProgressCallIds.add(params.id);
|
|
1210
|
+
const args = normalizeToolTranscriptArguments(params.arguments);
|
|
1211
|
+
const meta = inferToolMetaFromArgs(params.name, args, { detailMode: this.toolProgressDetailMode() });
|
|
1212
|
+
if (!this.params.onToolResult || !this.shouldEmitToolResult() || this.resultSummaryItemIds.has(params.id) || this.resultOutputStreamedItemIds.has(params.id)) return;
|
|
1213
|
+
this.resultSummaryItemIds.add(params.id);
|
|
1214
|
+
this.emitToolResultMessage({
|
|
1215
|
+
itemId: params.id,
|
|
1216
|
+
text: formatToolSummary(params.name, meta)
|
|
1217
|
+
});
|
|
1218
|
+
}
|
|
1219
|
+
emitTranscriptToolResultProgress(params) {
|
|
1220
|
+
if (this.transcriptProgressSuppressedIds.has(params.id) || !shouldEmitTranscriptToolProgress(params.name, this.transcriptArgumentsById.get(params.id))) return;
|
|
1221
|
+
if (!this.transcriptProgressCallIds.has(params.id)) this.emitTranscriptToolCallProgress({
|
|
1222
|
+
id: params.id,
|
|
1223
|
+
name: params.name,
|
|
1224
|
+
arguments: {}
|
|
1225
|
+
});
|
|
1226
|
+
if (!this.params.onToolResult || !this.shouldEmitToolOutput() || this.resultOutputItemIds.has(params.id) || this.resultOutputStreamedItemIds.has(params.id)) return;
|
|
1227
|
+
const text = params.text?.trim();
|
|
1228
|
+
if (text) this.emitToolResultMessage({
|
|
1229
|
+
itemId: params.id,
|
|
1230
|
+
text: formatToolOutput(params.name, void 0, text),
|
|
1231
|
+
finalOutput: true,
|
|
1232
|
+
isError: params.isError
|
|
1233
|
+
});
|
|
1234
|
+
}
|
|
1235
|
+
rememberEcho(itemId, signature) {
|
|
1236
|
+
if (!itemId) return;
|
|
1237
|
+
const existing = this.echoesByItem.get(itemId) ?? {
|
|
1238
|
+
displayTexts: [],
|
|
1239
|
+
rawSignatures: []
|
|
1240
|
+
};
|
|
1241
|
+
const displayText = signature.displayText?.trim();
|
|
1242
|
+
if (displayText) {
|
|
1243
|
+
if (signature.streamedDisplay) existing.streamedDisplayText = displayText;
|
|
1244
|
+
else if (!existing.displayTexts.includes(displayText)) {
|
|
1245
|
+
if (existing.displayTexts.length >= 24) existing.displayTexts.shift();
|
|
1246
|
+
existing.displayTexts.push(displayText);
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
const rawText = signature.rawText?.trim();
|
|
1250
|
+
const rawLength = signature.rawLength ?? rawText?.length;
|
|
1251
|
+
const rawPrefix = signature.rawPrefix?.trim() ?? rawText;
|
|
1252
|
+
if (rawLength !== void 0 && rawPrefix && rawPrefix.length >= 1024) {
|
|
1253
|
+
const next = {
|
|
1254
|
+
length: rawLength,
|
|
1255
|
+
prefix: rawPrefix.slice(0, TOOL_TRANSCRIPT_OUTPUT_MAX_CHARS)
|
|
1256
|
+
};
|
|
1257
|
+
if (signature.streamedDisplay) existing.streamedRawSignature = next;
|
|
1258
|
+
else {
|
|
1259
|
+
const matchIndex = existing.rawSignatures.findIndex((entry) => entry.prefix === next.prefix);
|
|
1260
|
+
if (matchIndex >= 0) existing.rawSignatures[matchIndex] = next;
|
|
1261
|
+
else {
|
|
1262
|
+
if (existing.rawSignatures.length >= 24) existing.rawSignatures.shift();
|
|
1263
|
+
existing.rawSignatures.push(next);
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
this.echoesByItem.set(itemId, existing);
|
|
1268
|
+
}
|
|
1269
|
+
};
|
|
1270
|
+
//#endregion
|
|
1271
|
+
//#region extensions/codex/src/app-server/session-history.ts
|
|
1272
|
+
/**
|
|
1273
|
+
* Reads OpenClaw session history for Codex transcript mirroring and sanitizes
|
|
1274
|
+
* image payloads before replaying messages into the app-server projector.
|
|
1275
|
+
*/
|
|
1276
|
+
function isMissingFileError(error) {
|
|
1277
|
+
return Boolean(error && typeof error === "object" && "code" in error && error.code === "ENOENT");
|
|
1278
|
+
}
|
|
1279
|
+
/** Returns sanitized session-context messages for a Codex mirrored session file. */
|
|
1280
|
+
async function readCodexMirroredSessionHistoryMessages(target) {
|
|
1281
|
+
try {
|
|
1282
|
+
const entries = await readCodexMirroredSessionEntries(target);
|
|
1283
|
+
if (entries.length === 0) return [];
|
|
1284
|
+
const firstEntry = entries[0];
|
|
1285
|
+
if (firstEntry?.type !== "session") return [];
|
|
1286
|
+
if (typeof firstEntry.id !== "string") return;
|
|
1287
|
+
migrateSessionEntries(entries);
|
|
1288
|
+
return sanitizeCodexHistoryImagePayloads(buildSessionContext(entries.filter((entry) => {
|
|
1289
|
+
return entry !== null && typeof entry === "object" && !Array.isArray(entry) && entry.type !== "session";
|
|
1290
|
+
})).messages, "codex mirrored history");
|
|
1291
|
+
} catch (error) {
|
|
1292
|
+
if (isMissingFileError(error)) return [];
|
|
1293
|
+
return;
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
async function readCodexMirroredSessionEntries(target) {
|
|
1297
|
+
const sqliteMarker = parseSqliteSessionFileMarker(target.sessionFile);
|
|
1298
|
+
if (sqliteMarker) {
|
|
1299
|
+
if (sqliteMarker.sessionId !== target.sessionId || target.agentId !== void 0 && sqliteMarker.agentId !== target.agentId) return [];
|
|
1300
|
+
const sessionKey = resolveSqliteMarkerSessionKey(target, sqliteMarker);
|
|
1301
|
+
if (!sessionKey) return [];
|
|
1302
|
+
return await readSessionTranscriptEvents({
|
|
1303
|
+
agentId: sqliteMarker.agentId,
|
|
1304
|
+
sessionId: sqliteMarker.sessionId,
|
|
1305
|
+
sessionKey,
|
|
1306
|
+
storePath: sqliteMarker.storePath
|
|
1307
|
+
});
|
|
1308
|
+
}
|
|
1309
|
+
return parseSessionEntries(await fs.readFile(target.sessionFile, "utf-8"));
|
|
1310
|
+
}
|
|
1311
|
+
function resolveSqliteMarkerSessionKey(target, marker) {
|
|
1312
|
+
const explicitSessionKey = target.sessionKey?.trim();
|
|
1313
|
+
if (explicitSessionKey) return explicitSessionKey;
|
|
1314
|
+
const entries = listSessionEntries({
|
|
1315
|
+
agentId: marker.agentId,
|
|
1316
|
+
readOnly: true,
|
|
1317
|
+
storePath: marker.storePath
|
|
1318
|
+
});
|
|
1319
|
+
return (entries.find(({ entry }) => {
|
|
1320
|
+
return entry.sessionId === marker.sessionId && entry.sessionFile === target.sessionFile;
|
|
1321
|
+
}) ?? entries.find(({ entry }) => {
|
|
1322
|
+
return entry.sessionId === marker.sessionId;
|
|
1323
|
+
}))?.sessionKey;
|
|
1324
|
+
}
|
|
1325
|
+
//#endregion
|
|
1326
|
+
//#region extensions/codex/src/app-server/event-projector-tool-transcript.ts
|
|
1327
|
+
const ZERO_USAGE = {
|
|
1328
|
+
input: 0,
|
|
1329
|
+
output: 0,
|
|
1330
|
+
cacheRead: 0,
|
|
1331
|
+
cacheWrite: 0,
|
|
1332
|
+
totalTokens: 0,
|
|
1333
|
+
cost: {
|
|
1334
|
+
input: 0,
|
|
1335
|
+
output: 0,
|
|
1336
|
+
cacheRead: 0,
|
|
1337
|
+
cacheWrite: 0,
|
|
1338
|
+
total: 0
|
|
1339
|
+
}
|
|
1340
|
+
};
|
|
1341
|
+
const MISSING_TOOL_RESULT_ERROR = "OpenClaw recorded a native Codex tool.call without a matching tool.result before the turn completed.";
|
|
1342
|
+
var CodexToolTranscriptProjection = class {
|
|
1343
|
+
constructor(params, threadId, turnId, progress, nextTranscriptTimestamp, options = {}) {
|
|
1344
|
+
this.params = params;
|
|
1345
|
+
this.threadId = threadId;
|
|
1346
|
+
this.turnId = turnId;
|
|
1347
|
+
this.progress = progress;
|
|
1348
|
+
this.nextTranscriptTimestamp = nextTranscriptTimestamp;
|
|
1349
|
+
this.options = options;
|
|
1350
|
+
this.messages = [];
|
|
1351
|
+
this.callIds = /* @__PURE__ */ new Set();
|
|
1352
|
+
this.resultIds = /* @__PURE__ */ new Set();
|
|
1353
|
+
this.namesById = /* @__PURE__ */ new Map();
|
|
1354
|
+
this.trajectoryCallIds = /* @__PURE__ */ new Set();
|
|
1355
|
+
this.trajectoryResultIds = /* @__PURE__ */ new Set();
|
|
1356
|
+
this.trajectoryNamesById = /* @__PURE__ */ new Map();
|
|
1357
|
+
this.trajectoryItemsById = /* @__PURE__ */ new Map();
|
|
1358
|
+
this.afterToolCallObservedItemIds = /* @__PURE__ */ new Set();
|
|
1359
|
+
}
|
|
1360
|
+
get transcriptMessages() {
|
|
1361
|
+
return this.messages;
|
|
1362
|
+
}
|
|
1363
|
+
recordDynamicToolCall(params) {
|
|
1364
|
+
this.recordToolCall({
|
|
1365
|
+
id: params.callId,
|
|
1366
|
+
name: params.tool,
|
|
1367
|
+
arguments: sanitizeCodexToolArguments(params.arguments)
|
|
1368
|
+
});
|
|
1369
|
+
}
|
|
1370
|
+
recordDynamicToolResult(params) {
|
|
1371
|
+
this.recordToolResult({
|
|
1372
|
+
id: params.callId,
|
|
1373
|
+
name: params.tool,
|
|
1374
|
+
text: collectDynamicToolContentText(params.contentItems),
|
|
1375
|
+
isError: !params.success
|
|
1376
|
+
});
|
|
1377
|
+
}
|
|
1378
|
+
recordNativeToolCall(item) {
|
|
1379
|
+
if (!item || !shouldRecordNativeToolTranscript(item)) return;
|
|
1380
|
+
const name = itemName(item);
|
|
1381
|
+
if (name) this.recordToolCall({
|
|
1382
|
+
id: item.id,
|
|
1383
|
+
name,
|
|
1384
|
+
arguments: itemToolArgs(item)
|
|
1385
|
+
});
|
|
1386
|
+
}
|
|
1387
|
+
recordNativeToolResult(item) {
|
|
1388
|
+
if (!item || !shouldRecordNativeToolTranscript(item)) return;
|
|
1389
|
+
const name = itemName(item);
|
|
1390
|
+
if (name) this.recordToolResult({
|
|
1391
|
+
id: item.id,
|
|
1392
|
+
name,
|
|
1393
|
+
text: itemTranscriptResultText(item, this.progress.outputTextByItem),
|
|
1394
|
+
isError: isNonSuccessItemStatus(itemStatus(item))
|
|
1395
|
+
});
|
|
1396
|
+
}
|
|
1397
|
+
recordTrajectoryEvent(params) {
|
|
1398
|
+
if (params.phase === "start") {
|
|
1399
|
+
this.trajectoryCallIds.add(params.item.id);
|
|
1400
|
+
this.trajectoryNamesById.set(params.item.id, params.name);
|
|
1401
|
+
this.trajectoryItemsById.set(params.item.id, params.item);
|
|
1402
|
+
this.options.trajectoryRecorder?.recordEvent("tool.call", {
|
|
1403
|
+
threadId: this.threadId,
|
|
1404
|
+
turnId: this.turnId,
|
|
1405
|
+
itemId: params.item.id,
|
|
1406
|
+
toolCallId: params.item.id,
|
|
1407
|
+
name: params.name,
|
|
1408
|
+
arguments: params.args
|
|
1409
|
+
});
|
|
1410
|
+
return;
|
|
1411
|
+
}
|
|
1412
|
+
this.trajectoryResultIds.add(params.item.id);
|
|
1413
|
+
const toolResult = itemToolResult(params.item).result;
|
|
1414
|
+
const output = itemOutputText(params.item, this.progress.outputTextByItem);
|
|
1415
|
+
this.options.trajectoryRecorder?.recordEvent("tool.result", {
|
|
1416
|
+
threadId: this.threadId,
|
|
1417
|
+
turnId: this.turnId,
|
|
1418
|
+
itemId: params.item.id,
|
|
1419
|
+
toolCallId: params.item.id,
|
|
1420
|
+
name: params.name,
|
|
1421
|
+
status: params.status,
|
|
1422
|
+
isError: isNonSuccessItemStatus(params.status),
|
|
1423
|
+
...toolResult ? { result: toolResult } : {},
|
|
1424
|
+
...output ? { output } : {}
|
|
1425
|
+
});
|
|
1426
|
+
}
|
|
1427
|
+
emitAfterToolCallObservation(item) {
|
|
1428
|
+
if (!this.shouldEmitAfterToolCallObservation(item)) return;
|
|
1429
|
+
const name = itemName(item);
|
|
1430
|
+
const status = itemStatus(item);
|
|
1431
|
+
if (!name || status === "running") return;
|
|
1432
|
+
this.afterToolCallObservedItemIds.add(item.id);
|
|
1433
|
+
const result = itemToolResult(item).result;
|
|
1434
|
+
const error = itemToolError(item, status, this.progress.outputTextByItem);
|
|
1435
|
+
const startedAt = resolveStartedAtFromDurationMs(item.durationMs);
|
|
1436
|
+
const hookParams = {
|
|
1437
|
+
toolName: name,
|
|
1438
|
+
toolCallId: item.id,
|
|
1439
|
+
runId: this.params.runId,
|
|
1440
|
+
agentId: this.params.agentId,
|
|
1441
|
+
sessionId: this.params.sessionId,
|
|
1442
|
+
sessionKey: this.params.sessionKey,
|
|
1443
|
+
startArgs: itemToolArgs(item) ?? {},
|
|
1444
|
+
...result !== void 0 ? { result } : {},
|
|
1445
|
+
...error ? { error } : {},
|
|
1446
|
+
...startedAt !== void 0 ? { startedAt } : {}
|
|
1447
|
+
};
|
|
1448
|
+
setImmediate(() => {
|
|
1449
|
+
runAgentHarnessAfterToolCallHook(hookParams);
|
|
1450
|
+
});
|
|
1451
|
+
}
|
|
1452
|
+
synthesizeMissingToolResults(params) {
|
|
1453
|
+
if (!params.synthesize) return;
|
|
1454
|
+
const missingTranscriptIds = [...this.callIds].filter((id) => !this.resultIds.has(id));
|
|
1455
|
+
const missingTrajectoryIds = [...this.trajectoryCallIds].filter((id) => !this.trajectoryResultIds.has(id));
|
|
1456
|
+
if (missingTranscriptIds.length === 0 && missingTrajectoryIds.length === 0) return;
|
|
1457
|
+
for (const id of missingTranscriptIds) {
|
|
1458
|
+
const name = this.namesById.get(id) ?? this.trajectoryNamesById.get(id);
|
|
1459
|
+
if (name) this.recordToolResult({
|
|
1460
|
+
id,
|
|
1461
|
+
name,
|
|
1462
|
+
text: formatMissingToolResultError({
|
|
1463
|
+
id,
|
|
1464
|
+
name
|
|
1465
|
+
}),
|
|
1466
|
+
isError: true
|
|
1467
|
+
});
|
|
1468
|
+
}
|
|
1469
|
+
for (const id of missingTrajectoryIds) {
|
|
1470
|
+
const name = this.trajectoryNamesById.get(id) ?? this.namesById.get(id);
|
|
1471
|
+
if (!name) continue;
|
|
1472
|
+
this.trajectoryResultIds.add(id);
|
|
1473
|
+
const text = formatMissingToolResultError({
|
|
1474
|
+
id,
|
|
1475
|
+
name
|
|
1476
|
+
});
|
|
1477
|
+
this.options.trajectoryRecorder?.recordEvent("tool.result", {
|
|
1478
|
+
threadId: this.threadId,
|
|
1479
|
+
turnId: this.turnId,
|
|
1480
|
+
itemId: id,
|
|
1481
|
+
toolCallId: id,
|
|
1482
|
+
name,
|
|
1483
|
+
status: "failed",
|
|
1484
|
+
isError: true,
|
|
1485
|
+
result: {
|
|
1486
|
+
status: "failed",
|
|
1487
|
+
reason: "missing_tool_result"
|
|
1488
|
+
},
|
|
1489
|
+
output: text
|
|
1490
|
+
});
|
|
1491
|
+
}
|
|
1492
|
+
if (!params.recordPromptError) {
|
|
1493
|
+
this.recordMissingToolError(missingTranscriptIds, missingTrajectoryIds);
|
|
1494
|
+
return;
|
|
1495
|
+
}
|
|
1496
|
+
const missingCount = (/* @__PURE__ */ new Set([...missingTranscriptIds, ...missingTrajectoryIds])).size;
|
|
1497
|
+
return missingCount === 1 ? MISSING_TOOL_RESULT_ERROR : `${MISSING_TOOL_RESULT_ERROR} missingToolResultCount=${missingCount}`;
|
|
1498
|
+
}
|
|
1499
|
+
async readMirroredSessionMessages() {
|
|
1500
|
+
return await readCodexMirroredSessionHistoryMessages({
|
|
1501
|
+
agentId: this.params.agentId,
|
|
1502
|
+
sessionFile: this.params.sessionFile,
|
|
1503
|
+
sessionId: this.params.sessionId,
|
|
1504
|
+
sessionKey: this.params.sessionKey
|
|
1505
|
+
}) ?? [];
|
|
1506
|
+
}
|
|
1507
|
+
recordToolCall(params) {
|
|
1508
|
+
if (!params.id || !params.name || this.callIds.has(params.id)) return;
|
|
1509
|
+
this.callIds.add(params.id);
|
|
1510
|
+
this.namesById.set(params.id, params.name);
|
|
1511
|
+
this.progress.recordTranscriptCall(params);
|
|
1512
|
+
this.messages.push(attachCodexMirrorIdentity(this.createToolCallMessage(params), `${this.turnId}:tool:${params.id}:call`));
|
|
1513
|
+
}
|
|
1514
|
+
recordToolResult(params) {
|
|
1515
|
+
if (!params.id || !params.name || this.resultIds.has(params.id)) return;
|
|
1516
|
+
this.resultIds.add(params.id);
|
|
1517
|
+
this.progress.recordTranscriptResult(params);
|
|
1518
|
+
this.messages.push(attachCodexMirrorIdentity(this.createToolResultMessage(params), `${this.turnId}:tool:${params.id}:result`));
|
|
1519
|
+
}
|
|
1520
|
+
recordMissingToolError(missingTranscriptIds, missingTrajectoryIds) {
|
|
1521
|
+
const firstMissingId = missingTranscriptIds.find((id) => Boolean(this.namesById.get(id))) ?? missingTrajectoryIds.find((id) => Boolean(this.trajectoryNamesById.get(id) ?? this.namesById.get(id)));
|
|
1522
|
+
if (!firstMissingId) return;
|
|
1523
|
+
const name = this.namesById.get(firstMissingId) ?? this.trajectoryNamesById.get(firstMissingId);
|
|
1524
|
+
if (!name) return;
|
|
1525
|
+
const item = this.trajectoryItemsById.get(firstMissingId);
|
|
1526
|
+
const meta = item ? itemMeta(item, this.progress.toolProgressDetailMode()) : this.progress.getToolMeta(firstMissingId)?.meta;
|
|
1527
|
+
const actionFingerprint = item ? nativeToolActionFingerprint(item) : void 0;
|
|
1528
|
+
this.progress.setLastToolError({
|
|
1529
|
+
toolName: name,
|
|
1530
|
+
...meta ? { meta } : {},
|
|
1531
|
+
error: formatMissingToolResultError({
|
|
1532
|
+
id: firstMissingId,
|
|
1533
|
+
name
|
|
1534
|
+
}),
|
|
1535
|
+
...item && isMutatingNativeToolItem(item) ? { mutatingAction: true } : {},
|
|
1536
|
+
...actionFingerprint ? { actionFingerprint } : {}
|
|
1537
|
+
});
|
|
1538
|
+
}
|
|
1539
|
+
shouldEmitAfterToolCallObservation(item) {
|
|
1540
|
+
if (!shouldSynthesizeToolProgressForItem(item) || this.afterToolCallObservedItemIds.has(item.id)) return false;
|
|
1541
|
+
return !(this.options.nativePostToolUseRelayEnabled && isNativePostToolUseRelayItem(item));
|
|
1542
|
+
}
|
|
1543
|
+
createToolCallMessage(params) {
|
|
1544
|
+
const args = normalizeToolTranscriptArguments(params.arguments);
|
|
1545
|
+
const attribution = resolveCodexLocalRuntimeAttribution(this.params);
|
|
1546
|
+
return {
|
|
1547
|
+
role: "assistant",
|
|
1548
|
+
content: [{
|
|
1549
|
+
type: "toolCall",
|
|
1550
|
+
id: params.id,
|
|
1551
|
+
name: params.name,
|
|
1552
|
+
arguments: args,
|
|
1553
|
+
input: args
|
|
1554
|
+
}],
|
|
1555
|
+
api: attribution.api ?? "openai-chatgpt-responses",
|
|
1556
|
+
provider: attribution.provider,
|
|
1557
|
+
model: this.params.modelId,
|
|
1558
|
+
usage: ZERO_USAGE,
|
|
1559
|
+
stopReason: "toolUse",
|
|
1560
|
+
timestamp: this.nextTranscriptTimestamp()
|
|
1561
|
+
};
|
|
1562
|
+
}
|
|
1563
|
+
createToolResultMessage(params) {
|
|
1564
|
+
const text = truncateToolTranscriptText(params.text?.trim() || toolResultStatusText(params));
|
|
1565
|
+
return {
|
|
1566
|
+
role: "toolResult",
|
|
1567
|
+
toolCallId: params.id,
|
|
1568
|
+
toolName: params.name,
|
|
1569
|
+
isError: params.isError,
|
|
1570
|
+
content: [{
|
|
1571
|
+
type: "toolResult",
|
|
1572
|
+
id: params.id,
|
|
1573
|
+
name: params.name,
|
|
1574
|
+
toolName: params.name,
|
|
1575
|
+
toolCallId: params.id,
|
|
1576
|
+
toolUseId: params.id,
|
|
1577
|
+
tool_use_id: params.id,
|
|
1578
|
+
content: text,
|
|
1579
|
+
text
|
|
1580
|
+
}],
|
|
1581
|
+
timestamp: this.nextTranscriptTimestamp()
|
|
1582
|
+
};
|
|
1583
|
+
}
|
|
1584
|
+
};
|
|
1585
|
+
function formatMissingToolResultError(params) {
|
|
1586
|
+
return `${MISSING_TOOL_RESULT_ERROR} toolCallId=${params.id}; toolName=${params.name}`;
|
|
1587
|
+
}
|
|
1588
|
+
function toolResultStatusText(params) {
|
|
1589
|
+
return params.isError ? `${params.name} failed` : `${params.name} completed`;
|
|
1590
|
+
}
|
|
1591
|
+
function resolveStartedAtFromDurationMs(durationMs) {
|
|
1592
|
+
if (typeof durationMs !== "number" || !Number.isFinite(durationMs)) return;
|
|
1593
|
+
return asDateTimestampMs(Date.now() - Math.max(0, durationMs));
|
|
1594
|
+
}
|
|
1595
|
+
//#endregion
|
|
1596
|
+
//#region extensions/codex/src/app-server/event-projector-events.ts
|
|
1597
|
+
var CodexEventProjection = class {
|
|
1598
|
+
constructor(threadId, turnId, emitAgentEvent, toolProgress, toolTranscript, onNativeToolResultRecorded) {
|
|
1599
|
+
this.threadId = threadId;
|
|
1600
|
+
this.turnId = turnId;
|
|
1601
|
+
this.emitAgentEvent = emitAgentEvent;
|
|
1602
|
+
this.toolProgress = toolProgress;
|
|
1603
|
+
this.toolTranscript = toolTranscript;
|
|
1604
|
+
this.onNativeToolResultRecorded = onNativeToolResultRecorded;
|
|
1605
|
+
this.reviewCount = 0;
|
|
1606
|
+
}
|
|
1607
|
+
get guardianReviewCount() {
|
|
1608
|
+
return this.reviewCount;
|
|
1609
|
+
}
|
|
1610
|
+
handleGuardianReview(method, params) {
|
|
1611
|
+
this.reviewCount += 1;
|
|
1612
|
+
const review = isJsonObject(params.review) ? params.review : void 0;
|
|
1613
|
+
const action = isJsonObject(params.action) ? params.action : void 0;
|
|
1614
|
+
this.emitAgentEvent({
|
|
1615
|
+
stream: "codex_app_server.guardian",
|
|
1616
|
+
data: {
|
|
1617
|
+
method,
|
|
1618
|
+
phase: method.endsWith("/started") ? "started" : "completed",
|
|
1619
|
+
reviewId: readString$5(params, "reviewId"),
|
|
1620
|
+
targetItemId: readNullableString$1(params, "targetItemId"),
|
|
1621
|
+
decisionSource: readString$5(params, "decisionSource"),
|
|
1622
|
+
status: review ? readString$5(review, "status") : void 0,
|
|
1623
|
+
riskLevel: review ? readString$5(review, "riskLevel") : void 0,
|
|
1624
|
+
userAuthorization: review ? readString$5(review, "userAuthorization") : void 0,
|
|
1625
|
+
rationale: review ? readNullableString$1(review, "rationale") : void 0,
|
|
1626
|
+
actionType: action ? readString$5(action, "type") : void 0
|
|
1627
|
+
}
|
|
1628
|
+
});
|
|
1629
|
+
}
|
|
1630
|
+
handleGuardianWarning(params) {
|
|
1631
|
+
this.emitAgentEvent({
|
|
1632
|
+
stream: "codex_app_server.guardian",
|
|
1633
|
+
data: {
|
|
1634
|
+
phase: "warning",
|
|
1635
|
+
message: readString$5(params, "message")
|
|
1636
|
+
}
|
|
1637
|
+
});
|
|
1638
|
+
}
|
|
1639
|
+
handleHook(method, params) {
|
|
1640
|
+
const run = isJsonObject(params.run) ? params.run : void 0;
|
|
1641
|
+
if (!run) return;
|
|
1642
|
+
const durationMs = readNumber(run, "durationMs");
|
|
1643
|
+
const entries = readHookOutputEntries(run.entries);
|
|
1644
|
+
const hookTurnId = readNullableString$1(params, "turnId");
|
|
1645
|
+
this.emitAgentEvent({
|
|
1646
|
+
stream: "codex_app_server.hook",
|
|
1647
|
+
data: {
|
|
1648
|
+
phase: method === "hook/started" ? "started" : "completed",
|
|
1649
|
+
threadId: this.threadId,
|
|
1650
|
+
turnId: hookTurnId === void 0 ? this.turnId : hookTurnId,
|
|
1651
|
+
hookRunId: readString$5(run, "id"),
|
|
1652
|
+
eventName: readString$5(run, "eventName"),
|
|
1653
|
+
handlerType: readString$5(run, "handlerType"),
|
|
1654
|
+
executionMode: readString$5(run, "executionMode"),
|
|
1655
|
+
scope: readString$5(run, "scope"),
|
|
1656
|
+
source: readString$5(run, "source"),
|
|
1657
|
+
sourcePath: readString$5(run, "sourcePath"),
|
|
1658
|
+
status: readString$5(run, "status"),
|
|
1659
|
+
statusMessage: readNullableString$1(run, "statusMessage"),
|
|
1660
|
+
...durationMs !== void 0 ? { durationMs } : {},
|
|
1661
|
+
...entries.length > 0 ? { entries } : {}
|
|
1662
|
+
}
|
|
1663
|
+
});
|
|
1664
|
+
}
|
|
1665
|
+
emitStandardItemEvent(params) {
|
|
1666
|
+
const { item } = params;
|
|
1667
|
+
if (!item) return;
|
|
1668
|
+
const kind = itemKind(item);
|
|
1669
|
+
if (!kind) return;
|
|
1670
|
+
const meta = itemMeta(item, this.toolProgress.toolProgressDetailMode());
|
|
1671
|
+
const suppressChannelProgress = shouldSuppressChannelProgressForItem(item);
|
|
1672
|
+
this.emitAgentEvent({
|
|
1673
|
+
stream: "item",
|
|
1674
|
+
data: {
|
|
1675
|
+
itemId: item.id,
|
|
1676
|
+
phase: params.phase,
|
|
1677
|
+
kind,
|
|
1678
|
+
title: itemTitle(item),
|
|
1679
|
+
status: params.phase === "start" ? "running" : itemStatus(item),
|
|
1680
|
+
...itemName(item) ? { name: itemName(item) } : {},
|
|
1681
|
+
...meta ? { meta } : {},
|
|
1682
|
+
...suppressChannelProgress ? { suppressChannelProgress: true } : {}
|
|
1683
|
+
}
|
|
1684
|
+
});
|
|
1685
|
+
}
|
|
1686
|
+
async emitNormalizedToolItemEvent(params) {
|
|
1687
|
+
const { item } = params;
|
|
1688
|
+
if (!item || !shouldSynthesizeToolProgressForItem(item)) return;
|
|
1689
|
+
const name = itemName(item);
|
|
1690
|
+
if (!name) return;
|
|
1691
|
+
const status = params.phase === "result" ? itemStatus(item) : "running";
|
|
1692
|
+
const args = itemToolArgs(item);
|
|
1693
|
+
const meta = itemMeta(item, this.toolProgress.toolProgressDetailMode());
|
|
1694
|
+
this.toolTranscript.recordTrajectoryEvent({
|
|
1695
|
+
phase: params.phase,
|
|
1696
|
+
item,
|
|
1697
|
+
name,
|
|
1698
|
+
args,
|
|
1699
|
+
status
|
|
1700
|
+
});
|
|
1701
|
+
if (params.phase === "result") this.toolProgress.recordNativeToolError({
|
|
1702
|
+
item,
|
|
1703
|
+
name,
|
|
1704
|
+
meta,
|
|
1705
|
+
status
|
|
1706
|
+
});
|
|
1707
|
+
if (!shouldEmitTranscriptToolProgress(name, args)) {
|
|
1708
|
+
if (params.phase === "result") {
|
|
1709
|
+
this.toolTranscript.emitAfterToolCallObservation(item);
|
|
1710
|
+
await this.onNativeToolResultRecorded?.();
|
|
1711
|
+
}
|
|
1712
|
+
return;
|
|
1713
|
+
}
|
|
1714
|
+
this.emitAgentEvent({
|
|
1715
|
+
stream: "tool",
|
|
1716
|
+
data: {
|
|
1717
|
+
phase: params.phase,
|
|
1718
|
+
name,
|
|
1719
|
+
itemId: item.id,
|
|
1720
|
+
toolCallId: item.id,
|
|
1721
|
+
...meta ? { meta } : {},
|
|
1722
|
+
...params.phase === "start" && args ? { args } : {},
|
|
1723
|
+
...params.phase === "result" ? {
|
|
1724
|
+
status,
|
|
1725
|
+
isError: isNonSuccessItemStatus(status),
|
|
1726
|
+
...itemToolResult(item)
|
|
1727
|
+
} : {}
|
|
1728
|
+
}
|
|
1729
|
+
});
|
|
1730
|
+
if (params.phase === "result") {
|
|
1731
|
+
this.toolTranscript.emitAfterToolCallObservation(item);
|
|
1732
|
+
await this.onNativeToolResultRecorded?.();
|
|
1733
|
+
}
|
|
1734
|
+
}
|
|
1735
|
+
};
|
|
1736
|
+
//#endregion
|
|
1737
|
+
//#region extensions/codex/src/app-server/event-projector-media.ts
|
|
1738
|
+
const GENERATED_IMAGE_MEDIA_SUBDIR = "tool-image-generation";
|
|
1739
|
+
const BYTES_PER_MB = 1024 * 1024;
|
|
1740
|
+
const DEFAULT_GENERATED_IMAGE_MAX_BYTES = 6 * BYTES_PER_MB;
|
|
1741
|
+
var CodexGeneratedMediaProjection = class {
|
|
1742
|
+
constructor(config) {
|
|
1743
|
+
this.config = config;
|
|
1744
|
+
this.itemIds = /* @__PURE__ */ new Set();
|
|
1745
|
+
this.urlsByItemId = /* @__PURE__ */ new Map();
|
|
1746
|
+
}
|
|
1747
|
+
hasGeneratedMedia() {
|
|
1748
|
+
return this.itemIds.size > 0;
|
|
1749
|
+
}
|
|
1750
|
+
recordNative(item) {
|
|
1751
|
+
if (item?.type !== "imageGeneration") return;
|
|
1752
|
+
const savedPath = readItemString(item, "savedPath")?.trim();
|
|
1753
|
+
if (savedPath) this.recordUrl({
|
|
1754
|
+
itemId: item.id,
|
|
1755
|
+
mediaUrl: savedPath
|
|
1756
|
+
});
|
|
1757
|
+
}
|
|
1758
|
+
async recordRaw(item) {
|
|
1759
|
+
if (readString$5(item, "type") !== "image_generation_call") return;
|
|
1760
|
+
const result = readString$5(item, "result");
|
|
1761
|
+
if (!result) return;
|
|
1762
|
+
const itemId = readString$5(item, "id") ?? `raw-image-${this.itemIds.size}`;
|
|
1763
|
+
this.itemIds.add(itemId);
|
|
1764
|
+
const maxBytes = resolveGeneratedImageMaxBytes(this.config);
|
|
1765
|
+
const estimatedDecodedBytes = estimateBase64DecodedBytes(result);
|
|
1766
|
+
if (estimatedDecodedBytes !== void 0 && estimatedDecodedBytes > maxBytes) {
|
|
1767
|
+
embeddedAgentLog.warn("codex app-server raw image generation result exceeds media limit", {
|
|
1768
|
+
itemId,
|
|
1769
|
+
estimatedDecodedBytes,
|
|
1770
|
+
maxBytes
|
|
1771
|
+
});
|
|
1772
|
+
return;
|
|
1773
|
+
}
|
|
1774
|
+
const asset = generatedImageAssetFromBase64({
|
|
1775
|
+
base64: result,
|
|
1776
|
+
index: this.itemIds.size,
|
|
1777
|
+
revisedPrompt: readString$5(item, "revised_prompt") ?? readString$5(item, "revisedPrompt"),
|
|
1778
|
+
fileNamePrefix: "codex-image-generation",
|
|
1779
|
+
sniffMimeType: true
|
|
1780
|
+
});
|
|
1781
|
+
if (!asset) return;
|
|
1782
|
+
try {
|
|
1783
|
+
const saved = await saveMediaBuffer(asset.buffer, asset.mimeType, GENERATED_IMAGE_MEDIA_SUBDIR, maxBytes, asset.fileName);
|
|
1784
|
+
this.recordUrl({
|
|
1785
|
+
itemId,
|
|
1786
|
+
mediaUrl: saved.path,
|
|
1787
|
+
replaceExisting: true
|
|
1788
|
+
});
|
|
1789
|
+
} catch (error) {
|
|
1790
|
+
embeddedAgentLog.warn("codex app-server raw image generation result save failed", {
|
|
1791
|
+
itemId,
|
|
1792
|
+
error
|
|
1793
|
+
});
|
|
1794
|
+
}
|
|
1795
|
+
}
|
|
1796
|
+
buildToolMediaUrls(params) {
|
|
1797
|
+
const mediaUrls = new Set(params.toolMediaUrls?.map((url) => url.trim()).filter(Boolean) ?? []);
|
|
1798
|
+
if ((params.messagingToolSentMediaUrls?.length ?? 0) === 0) for (const mediaUrl of this.urlsByItemId.values()) mediaUrls.add(mediaUrl);
|
|
1799
|
+
return mediaUrls.size > 0 ? [...mediaUrls] : params.toolMediaUrls;
|
|
1800
|
+
}
|
|
1801
|
+
buildHostOwnedMediaUrls(params) {
|
|
1802
|
+
if ((params.messagingToolSentMediaUrls?.length ?? 0) > 0) return;
|
|
1803
|
+
const mediaUrls = [...this.urlsByItemId.values()];
|
|
1804
|
+
return mediaUrls.length > 0 ? mediaUrls : void 0;
|
|
1805
|
+
}
|
|
1806
|
+
recordUrl(params) {
|
|
1807
|
+
if (this.urlsByItemId.has(params.itemId) && params.replaceExisting !== true) {
|
|
1808
|
+
this.itemIds.add(params.itemId);
|
|
1809
|
+
return;
|
|
1810
|
+
}
|
|
1811
|
+
this.urlsByItemId.set(params.itemId, params.mediaUrl);
|
|
1812
|
+
this.itemIds.add(params.itemId);
|
|
1813
|
+
}
|
|
1814
|
+
};
|
|
1815
|
+
function estimateBase64DecodedBytes(base64) {
|
|
1816
|
+
let nonWhitespaceLength = 0;
|
|
1817
|
+
let previousCode = -1;
|
|
1818
|
+
let lastCode = -1;
|
|
1819
|
+
for (let i = 0; i < base64.length; i += 1) {
|
|
1820
|
+
const code = base64.charCodeAt(i);
|
|
1821
|
+
if (isBase64WhitespaceCode(code)) continue;
|
|
1822
|
+
nonWhitespaceLength += 1;
|
|
1823
|
+
previousCode = lastCode;
|
|
1824
|
+
lastCode = code;
|
|
1825
|
+
}
|
|
1826
|
+
if (nonWhitespaceLength === 0) return;
|
|
1827
|
+
const equalsCode = "=".charCodeAt(0);
|
|
1828
|
+
const padding = lastCode === equalsCode ? previousCode === equalsCode ? 2 : 1 : 0;
|
|
1829
|
+
return Math.max(0, Math.floor(nonWhitespaceLength * 3 / 4) - padding);
|
|
1830
|
+
}
|
|
1831
|
+
function isBase64WhitespaceCode(code) {
|
|
1832
|
+
return code === 32 || code === 9 || code === 10 || code === 13;
|
|
1833
|
+
}
|
|
1834
|
+
function resolveGeneratedImageMaxBytes(config) {
|
|
1835
|
+
const configured = config?.agents?.defaults?.mediaMaxMb;
|
|
1836
|
+
if (typeof configured === "number" && Number.isFinite(configured) && configured > 0) return Math.floor(configured * BYTES_PER_MB);
|
|
1837
|
+
return DEFAULT_GENERATED_IMAGE_MAX_BYTES;
|
|
1838
|
+
}
|
|
1839
|
+
//#endregion
|
|
1840
|
+
//#region extensions/codex/src/app-server/event-projector-reasoning.ts
|
|
1841
|
+
var CodexReasoningProjection = class {
|
|
1842
|
+
constructor(params, emitAgentEvent) {
|
|
1843
|
+
this.params = params;
|
|
1844
|
+
this.emitAgentEvent = emitAgentEvent;
|
|
1845
|
+
this.reasoningTextByGroup = /* @__PURE__ */ new Map();
|
|
1846
|
+
this.reasoningItemOrder = /* @__PURE__ */ new Map();
|
|
1847
|
+
this.planTextByItem = /* @__PURE__ */ new Map();
|
|
1848
|
+
this.reasoningStarted = false;
|
|
1849
|
+
this.reasoningEnded = false;
|
|
1850
|
+
}
|
|
1851
|
+
async handleReasoningDelta(method, params) {
|
|
1852
|
+
const itemId = readString$5(params, "itemId") ?? "reasoning";
|
|
1853
|
+
const delta = readString$5(params, "delta") ?? "";
|
|
1854
|
+
if (!delta) return;
|
|
1855
|
+
this.reasoningStarted = true;
|
|
1856
|
+
if (!this.reasoningItemOrder.has(itemId)) this.reasoningItemOrder.set(itemId, this.reasoningItemOrder.size);
|
|
1857
|
+
const groupIndex = method === "item/reasoning/textDelta" ? readNonNegativeInteger(params, "contentIndex") ?? 0 : readNonNegativeInteger(params, "summaryIndex") ?? 0;
|
|
1858
|
+
const groupKey = `${method}\0${itemId}\0${groupIndex}`;
|
|
1859
|
+
const current = this.reasoningTextByGroup.get(groupKey);
|
|
1860
|
+
this.reasoningTextByGroup.set(groupKey, {
|
|
1861
|
+
itemId,
|
|
1862
|
+
method,
|
|
1863
|
+
index: groupIndex,
|
|
1864
|
+
text: `${current?.text ?? ""}${delta}`
|
|
1865
|
+
});
|
|
1866
|
+
await this.params.onReasoningStream?.({
|
|
1867
|
+
text: this.reasoningText(),
|
|
1868
|
+
isReasoningSnapshot: true
|
|
1869
|
+
});
|
|
1870
|
+
}
|
|
1871
|
+
handlePlanDelta(params) {
|
|
1872
|
+
const itemId = readString$5(params, "itemId") ?? "plan";
|
|
1873
|
+
const delta = readString$5(params, "delta") ?? "";
|
|
1874
|
+
if (!delta) return;
|
|
1875
|
+
const text = `${this.planTextByItem.get(itemId) ?? ""}${delta}`;
|
|
1876
|
+
this.planTextByItem.set(itemId, text);
|
|
1877
|
+
this.emitPlanUpdate({
|
|
1878
|
+
explanation: void 0,
|
|
1879
|
+
steps: splitPlanText(text).map((step) => ({
|
|
1880
|
+
step,
|
|
1881
|
+
status: "pending"
|
|
1882
|
+
}))
|
|
1883
|
+
});
|
|
1884
|
+
}
|
|
1885
|
+
handleTurnPlanUpdated(params) {
|
|
1886
|
+
const explanation = readNullableString$1(params, "explanation");
|
|
1887
|
+
const plan = Array.isArray(params.plan) ? params.plan.flatMap((entry) => {
|
|
1888
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) return [];
|
|
1889
|
+
const record = entry;
|
|
1890
|
+
const step = readString$5(record, "step");
|
|
1891
|
+
if (!step) return [];
|
|
1892
|
+
return [{
|
|
1893
|
+
step,
|
|
1894
|
+
status: normalizePlanStepStatus(readString$5(record, "status"))
|
|
1895
|
+
}];
|
|
1896
|
+
}) : void 0;
|
|
1897
|
+
const planText = [explanation, ...(plan ?? []).map(({ step, status }) => `- [${status}] ${step}`)].filter((part) => Boolean(part)).join("\n");
|
|
1898
|
+
if (planText) this.turnPlanText = planText;
|
|
1899
|
+
this.emitPlanUpdate({
|
|
1900
|
+
explanation,
|
|
1901
|
+
steps: plan
|
|
1902
|
+
});
|
|
1903
|
+
}
|
|
1904
|
+
recordItem(item) {
|
|
1905
|
+
if (item?.type === "plan" && typeof item.text === "string" && item.text) {
|
|
1906
|
+
this.planTextByItem.set(item.id, item.text);
|
|
1907
|
+
this.emitPlanUpdate({
|
|
1908
|
+
explanation: void 0,
|
|
1909
|
+
steps: splitPlanText(item.text).map((step) => ({
|
|
1910
|
+
step,
|
|
1911
|
+
status: "pending"
|
|
1912
|
+
}))
|
|
1913
|
+
});
|
|
1914
|
+
}
|
|
1915
|
+
}
|
|
1916
|
+
async maybeEndReasoning() {
|
|
1917
|
+
if (!this.reasoningStarted || this.reasoningEnded) return;
|
|
1918
|
+
this.reasoningEnded = true;
|
|
1919
|
+
await this.params.onReasoningEnd?.();
|
|
1920
|
+
}
|
|
1921
|
+
reasoningText() {
|
|
1922
|
+
return collectReasoningTextValues(this.reasoningTextByGroup, this.reasoningItemOrder).join("\n\n");
|
|
1923
|
+
}
|
|
1924
|
+
planText() {
|
|
1925
|
+
return this.turnPlanText ?? [...this.planTextByItem.values()].filter((text) => text.trim().length > 0).join("\n\n");
|
|
1926
|
+
}
|
|
1927
|
+
emitPlanUpdate(params) {
|
|
1928
|
+
if (!params.explanation && (!params.steps || params.steps.length === 0)) return;
|
|
1929
|
+
this.emitAgentEvent({
|
|
1930
|
+
stream: "plan",
|
|
1931
|
+
data: {
|
|
1932
|
+
phase: "update",
|
|
1933
|
+
title: "Plan updated",
|
|
1934
|
+
source: "codex-app-server",
|
|
1935
|
+
...params.explanation ? { explanation: params.explanation } : {},
|
|
1936
|
+
...params.steps && params.steps.length > 0 ? { steps: params.steps } : {}
|
|
1937
|
+
}
|
|
1938
|
+
});
|
|
1939
|
+
}
|
|
1940
|
+
};
|
|
1941
|
+
function normalizePlanStepStatus(status) {
|
|
1942
|
+
if (status === "inProgress" || status === "in_progress") return "in_progress";
|
|
1943
|
+
return status === "completed" ? "completed" : "pending";
|
|
1944
|
+
}
|
|
1945
|
+
function collectReasoningTextValues(groups, itemOrder) {
|
|
1946
|
+
return [...groups.values()].toSorted((left, right) => {
|
|
1947
|
+
const itemDelta = (itemOrder.get(left.itemId) ?? Number.MAX_SAFE_INTEGER) - (itemOrder.get(right.itemId) ?? Number.MAX_SAFE_INTEGER);
|
|
1948
|
+
if (itemDelta !== 0) return itemDelta;
|
|
1949
|
+
const methodDelta = reasoningMethodOrder(left.method) - reasoningMethodOrder(right.method);
|
|
1950
|
+
return methodDelta !== 0 ? methodDelta : left.index - right.index;
|
|
1951
|
+
}).map((group) => group.text).filter((text) => text.trim().length > 0);
|
|
1952
|
+
}
|
|
1953
|
+
function reasoningMethodOrder(method) {
|
|
1954
|
+
return method === "item/reasoning/summaryTextDelta" ? 0 : 1;
|
|
1955
|
+
}
|
|
1956
|
+
//#endregion
|
|
1957
|
+
//#region extensions/codex/src/app-server/event-projector-snapshot.ts
|
|
1958
|
+
function buildCodexMessagesSnapshot(params) {
|
|
1959
|
+
const messages = promptSnapshot(params.runParams, params.turnId, params.upstreamUserText);
|
|
1960
|
+
if (params.reasoningText) messages.push(attachCodexMirrorIdentity(params.createAssistantMirrorMessage("Codex reasoning", params.reasoningText), `${params.turnId}:reasoning`));
|
|
1961
|
+
if (params.planText) messages.push(attachCodexMirrorIdentity(params.createAssistantMirrorMessage("Codex plan", params.planText), `${params.turnId}:plan`));
|
|
1962
|
+
const visibleWorkMessages = [...params.runParams.config?.ui?.prefs?.chatPersistCommentary === false ? [] : params.commentaryMessages.map(({ itemId, message }) => attachCodexMirrorIdentity(message, `${params.turnId}:commentary:${itemId}`)), ...params.toolMessages].toSorted((left, right) => (asDateTimestampMs(left.timestamp) ?? 0) - (asDateTimestampMs(right.timestamp) ?? 0));
|
|
1963
|
+
messages.push(...visibleWorkMessages);
|
|
1964
|
+
if (params.lastAssistant) messages.push(attachCodexMirrorIdentity(params.lastAssistant, `${params.turnId}:assistant`));
|
|
1965
|
+
return messages.map((message) => projectAgentHarnessTranscriptMessageForDisplay({
|
|
1966
|
+
hidden: params.runParams.trigger === "memory",
|
|
1967
|
+
message
|
|
1968
|
+
}));
|
|
1969
|
+
}
|
|
1970
|
+
//#endregion
|
|
1971
|
+
//#region extensions/codex/src/app-server/usage-limit-error.ts
|
|
194
1972
|
/**
|
|
195
|
-
*
|
|
196
|
-
*
|
|
1973
|
+
* Enriches Codex usage-limit failures with current rate-limit information and
|
|
1974
|
+
* marks blocked auth profiles when Codex exposes a reset time.
|
|
197
1975
|
*/
|
|
1976
|
+
const CODEX_USAGE_LIMIT_RATE_LIMIT_REFRESH_TIMEOUT_MS = 5e3;
|
|
1977
|
+
function createCodexUsageLimitPromptError(message) {
|
|
1978
|
+
return Object.assign(new Error(message), { status: 429 });
|
|
1979
|
+
}
|
|
1980
|
+
function isCodexUsageLimitPromptError(error) {
|
|
1981
|
+
return error instanceof Error && "status" in error && error.status === 429;
|
|
1982
|
+
}
|
|
1983
|
+
/** Marks a Codex auth profile blocked until the reset time advertised by rate limits. */
|
|
1984
|
+
async function markCodexAuthProfileBlockedFromRateLimits(params) {
|
|
1985
|
+
const authProfileId = params.authProfileId?.trim();
|
|
1986
|
+
if (!authProfileId || !params.params.authProfileStore) return;
|
|
1987
|
+
const blockedUntil = resolveCodexUsageLimitResetAtMs(params.rateLimits);
|
|
1988
|
+
if (!blockedUntil) return;
|
|
1989
|
+
try {
|
|
1990
|
+
await markAuthProfileBlockedUntil({
|
|
1991
|
+
store: params.params.authProfileStore,
|
|
1992
|
+
profileId: authProfileId,
|
|
1993
|
+
blockedUntil,
|
|
1994
|
+
source: "codex_rate_limits",
|
|
1995
|
+
agentDir: params.params.agentDir,
|
|
1996
|
+
runId: params.params.runId,
|
|
1997
|
+
modelId: params.params.modelId
|
|
1998
|
+
});
|
|
1999
|
+
} catch (error) {
|
|
2000
|
+
embeddedAgentLog.debug("failed to mark Codex auth profile blocked from app-server limits", {
|
|
2001
|
+
authProfileId,
|
|
2002
|
+
error: formatErrorMessage(error)
|
|
2003
|
+
});
|
|
2004
|
+
}
|
|
2005
|
+
}
|
|
2006
|
+
/** Formats a turn-start usage-limit error, refreshing rate limits when needed. */
|
|
2007
|
+
async function formatCodexTurnStartUsageLimitError(params) {
|
|
2008
|
+
return refreshCodexUsageLimitError({
|
|
2009
|
+
client: params.client,
|
|
2010
|
+
source: readCodexTurnStartUsageLimitErrorSource(params.client, params.error, params.errorNotification, params.rateLimitsRevisionBeforeTurnStart),
|
|
2011
|
+
timeoutMs: params.timeoutMs,
|
|
2012
|
+
signal: params.signal
|
|
2013
|
+
});
|
|
2014
|
+
}
|
|
2015
|
+
/** Refreshes a generic prompt usage-limit message into a reset-aware message. */
|
|
2016
|
+
async function refreshCodexUsageLimitPromptError(params) {
|
|
2017
|
+
if (!shouldRefreshCodexRateLimitsForUsageLimitMessage(params.message)) return;
|
|
2018
|
+
return refreshCodexUsageLimitError({
|
|
2019
|
+
client: params.client,
|
|
2020
|
+
source: {
|
|
2021
|
+
message: params.message,
|
|
2022
|
+
codexErrorInfo: "usageLimitExceeded",
|
|
2023
|
+
rateLimits: readRecentCodexRateLimits(params.client)
|
|
2024
|
+
},
|
|
2025
|
+
timeoutMs: params.timeoutMs,
|
|
2026
|
+
signal: params.signal
|
|
2027
|
+
});
|
|
2028
|
+
}
|
|
2029
|
+
async function refreshCodexUsageLimitError(params) {
|
|
2030
|
+
const initialMessage = formatCodexUsageLimitErrorMessage(params.source);
|
|
2031
|
+
if (!shouldRefreshCodexRateLimitsForUsageLimitMessage(initialMessage)) return initialMessage ? {
|
|
2032
|
+
message: initialMessage,
|
|
2033
|
+
...params.source.rateLimitsTrustedForProfile ? { rateLimitsForProfile: params.source.rateLimits } : {}
|
|
2034
|
+
} : void 0;
|
|
2035
|
+
const rateLimits = await readCodexRateLimitsFromAppServerForUsageLimitError({
|
|
2036
|
+
client: params.client,
|
|
2037
|
+
timeoutMs: params.timeoutMs,
|
|
2038
|
+
signal: params.signal
|
|
2039
|
+
});
|
|
2040
|
+
if (!rateLimits) return initialMessage ? {
|
|
2041
|
+
message: initialMessage,
|
|
2042
|
+
...params.source.rateLimitsTrustedForProfile ? { rateLimitsForProfile: params.source.rateLimits } : {}
|
|
2043
|
+
} : void 0;
|
|
2044
|
+
const message = formatCodexUsageLimitErrorMessage({
|
|
2045
|
+
message: params.source.message,
|
|
2046
|
+
codexErrorInfo: params.source.codexErrorInfo,
|
|
2047
|
+
rateLimits,
|
|
2048
|
+
rateLimitsAuthoritative: true
|
|
2049
|
+
}) ?? initialMessage;
|
|
2050
|
+
return message ? {
|
|
2051
|
+
message,
|
|
2052
|
+
rateLimitsForProfile: rateLimits
|
|
2053
|
+
} : void 0;
|
|
2054
|
+
}
|
|
2055
|
+
async function readCodexRateLimitsFromAppServerForUsageLimitError(params) {
|
|
2056
|
+
if (params.signal?.aborted) return;
|
|
2057
|
+
try {
|
|
2058
|
+
const rateLimits = await params.client.request(CODEX_CONTROL_METHODS.rateLimits, void 0, {
|
|
2059
|
+
timeoutMs: resolveCodexUsageLimitRateLimitRefreshTimeoutMs(params.timeoutMs),
|
|
2060
|
+
signal: params.signal
|
|
2061
|
+
});
|
|
2062
|
+
rememberCodexRateLimitsRead(params.client, rateLimits);
|
|
2063
|
+
return rateLimits;
|
|
2064
|
+
} catch (error) {
|
|
2065
|
+
embeddedAgentLog.debug("codex app-server rate-limit refresh failed after usage-limit error", { error: formatErrorMessage(error) });
|
|
2066
|
+
return;
|
|
2067
|
+
}
|
|
2068
|
+
}
|
|
2069
|
+
function resolveCodexUsageLimitRateLimitRefreshTimeoutMs(timeoutMs) {
|
|
2070
|
+
if (timeoutMs === void 0 || !Number.isFinite(timeoutMs) || timeoutMs <= 0) return CODEX_USAGE_LIMIT_RATE_LIMIT_REFRESH_TIMEOUT_MS;
|
|
2071
|
+
return Math.max(100, Math.min(timeoutMs, CODEX_USAGE_LIMIT_RATE_LIMIT_REFRESH_TIMEOUT_MS));
|
|
2072
|
+
}
|
|
2073
|
+
function readCodexTurnStartUsageLimitErrorSource(client, error, errorNotification, rateLimitsRevisionBeforeTurnStart) {
|
|
2074
|
+
const notificationError = readCodexErrorNotification(errorNotification);
|
|
2075
|
+
const errorPayload = readCodexErrorPayload(error);
|
|
2076
|
+
const rateLimits = errorPayload.rateLimits ?? readRecentCodexRateLimits(client);
|
|
2077
|
+
const cacheUpdatedDuringTurnStart = rateLimitsRevisionBeforeTurnStart !== void 0 && readCodexRateLimitsRevision(client) > rateLimitsRevisionBeforeTurnStart;
|
|
2078
|
+
return {
|
|
2079
|
+
message: notificationError?.message ?? errorPayload.message ?? formatErrorMessage(error),
|
|
2080
|
+
codexErrorInfo: notificationError?.codexErrorInfo ?? errorPayload.codexErrorInfo,
|
|
2081
|
+
rateLimits,
|
|
2082
|
+
rateLimitsTrustedForProfile: errorPayload.rateLimits !== void 0 || cacheUpdatedDuringTurnStart
|
|
2083
|
+
};
|
|
2084
|
+
}
|
|
2085
|
+
function readCodexErrorNotification(notification) {
|
|
2086
|
+
if (notification?.method !== "error" || !isJsonObject(notification.params)) return;
|
|
2087
|
+
const error = notification.params.error;
|
|
2088
|
+
return isJsonObject(error) ? {
|
|
2089
|
+
message: readString$4(error, "message"),
|
|
2090
|
+
codexErrorInfo: error.codexErrorInfo
|
|
2091
|
+
} : void 0;
|
|
2092
|
+
}
|
|
2093
|
+
function readCodexErrorPayload(error) {
|
|
2094
|
+
const message = error instanceof Error ? error.message : void 0;
|
|
2095
|
+
if (!error || typeof error !== "object" || !("data" in error)) return { message };
|
|
2096
|
+
const data = error.data;
|
|
2097
|
+
if (!isJsonObject(data)) return { message };
|
|
2098
|
+
const nestedError = isJsonObject(data.error) ? data.error : data;
|
|
2099
|
+
const rateLimits = nestedError.rateLimits ?? data.rateLimits;
|
|
2100
|
+
return {
|
|
2101
|
+
message: readString$4(nestedError, "message") ?? message,
|
|
2102
|
+
codexErrorInfo: nestedError.codexErrorInfo,
|
|
2103
|
+
rateLimits
|
|
2104
|
+
};
|
|
2105
|
+
}
|
|
2106
|
+
function readString$4(record, key) {
|
|
2107
|
+
const value = record[key];
|
|
2108
|
+
return typeof value === "string" ? value : void 0;
|
|
2109
|
+
}
|
|
2110
|
+
//#endregion
|
|
2111
|
+
//#region extensions/codex/src/app-server/event-projector.ts
|
|
2112
|
+
var CodexAppServerEventProjector = class {
|
|
2113
|
+
constructor(params, threadId, turnId, options = {}) {
|
|
2114
|
+
this.params = params;
|
|
2115
|
+
this.threadId = threadId;
|
|
2116
|
+
this.turnId = turnId;
|
|
2117
|
+
this.options = options;
|
|
2118
|
+
this.activeItemIds = /* @__PURE__ */ new Set();
|
|
2119
|
+
this.completedItemIds = /* @__PURE__ */ new Set();
|
|
2120
|
+
this.activeCompactionItemIds = /* @__PURE__ */ new Set();
|
|
2121
|
+
this.terminalPresentationClearedItemIds = /* @__PURE__ */ new Set();
|
|
2122
|
+
this.nativeToolOutcomeOrdinals = /* @__PURE__ */ new Map();
|
|
2123
|
+
this.promptErrorSource = null;
|
|
2124
|
+
this.synthesizedMissingToolResultError = null;
|
|
2125
|
+
this.aborted = false;
|
|
2126
|
+
this.completedCompactionCount = 0;
|
|
2127
|
+
this.lastTranscriptTimestamp = 0;
|
|
2128
|
+
this.diagnostics = new CodexProjectionDiagnostics(threadId, turnId);
|
|
2129
|
+
this.nativeToolLifecycleProjector = new CodexNativeToolLifecycleProjector(params, threadId, turnId, { runAbortSignal: options.runAbortSignal });
|
|
2130
|
+
this.generatedMediaProjection = new CodexGeneratedMediaProjection(params.config);
|
|
2131
|
+
this.toolProgressProjection = new CodexToolProgressProjection(params);
|
|
2132
|
+
this.toolTranscriptProjection = new CodexToolTranscriptProjection(params, threadId, turnId, this.toolProgressProjection, () => this.nextTranscriptTimestamp(), {
|
|
2133
|
+
nativePostToolUseRelayEnabled: options.nativePostToolUseRelayEnabled,
|
|
2134
|
+
trajectoryRecorder: options.trajectoryRecorder
|
|
2135
|
+
});
|
|
2136
|
+
this.eventProjection = new CodexEventProjection(threadId, turnId, (event) => this.emitAgentEvent(event), this.toolProgressProjection, this.toolTranscriptProjection, options.onNativeToolResultRecorded);
|
|
2137
|
+
this.assistantProjection = new CodexAssistantProjection(params, (event) => this.emitAgentEvent(event), (text) => this.toolProgressProjection.matchesEcho(text), () => this.nextTranscriptTimestamp());
|
|
2138
|
+
this.reasoningProjection = new CodexReasoningProjection(params, (event) => this.emitAgentEvent(event));
|
|
2139
|
+
}
|
|
2140
|
+
nextTranscriptTimestamp() {
|
|
2141
|
+
this.lastTranscriptTimestamp = Math.max(Date.now(), this.lastTranscriptTimestamp + 1);
|
|
2142
|
+
return this.lastTranscriptTimestamp;
|
|
2143
|
+
}
|
|
2144
|
+
getCompletedTurnStatus() {
|
|
2145
|
+
return this.completedTurn?.status;
|
|
2146
|
+
}
|
|
2147
|
+
hasCompletedTerminalAssistantText() {
|
|
2148
|
+
return this.assistantProjection.hasCompletedTerminalAssistantText(this.completedItemIds);
|
|
2149
|
+
}
|
|
2150
|
+
getLatestTerminalAssistantCandidate() {
|
|
2151
|
+
return this.assistantProjection.getLatestTerminalAssistantCandidate();
|
|
2152
|
+
}
|
|
2153
|
+
hasLatestTerminalAssistantCandidateText() {
|
|
2154
|
+
return this.assistantProjection.hasLatestTerminalAssistantCandidateText();
|
|
2155
|
+
}
|
|
2156
|
+
canReleaseLatestTerminalAssistantAfterToolHandoff() {
|
|
2157
|
+
return this.assistantProjection.canReleaseLatestTerminalAssistantAfterToolHandoff();
|
|
2158
|
+
}
|
|
2159
|
+
/** Restores a completed final item after only the enclosing turn timeout fired. */
|
|
2160
|
+
recoverCompletedTerminalAssistantAfterTurnWatchTimeout() {
|
|
2161
|
+
if (!this.aborted || this.promptError !== "codex app-server attempt timed out" || !this.hasCompletedTerminalAssistantText()) return false;
|
|
2162
|
+
this.aborted = false;
|
|
2163
|
+
this.promptError = void 0;
|
|
2164
|
+
this.promptErrorSource = null;
|
|
2165
|
+
return true;
|
|
2166
|
+
}
|
|
2167
|
+
/** Resolves the shared model-order position for a native tool item. */
|
|
2168
|
+
recordNativeToolOutcome(item) {
|
|
2169
|
+
if (!item || this.nativeToolOutcomeOrdinals.has(item.id) || !shouldClearTerminalPresentationForNativeItem(item)) return;
|
|
2170
|
+
const ordinal = this.params.allocateToolOutcomeOrdinal?.(item.id);
|
|
2171
|
+
if (ordinal !== void 0) this.nativeToolOutcomeOrdinals.set(item.id, ordinal);
|
|
2172
|
+
}
|
|
2173
|
+
recordNativeToolApprovalFailure(toolCallId, disposition) {
|
|
2174
|
+
this.nativeToolLifecycleProjector.recordApprovalFailureDisposition(toolCallId, disposition);
|
|
2175
|
+
}
|
|
2176
|
+
recordNativeToolPreToolUseFailure(failure) {
|
|
2177
|
+
this.nativeToolLifecycleProjector.recordPreToolUseFailure(failure);
|
|
2178
|
+
}
|
|
2179
|
+
async handleNotification(notification) {
|
|
2180
|
+
const params = isJsonObject(notification.params) ? notification.params : void 0;
|
|
2181
|
+
if (!params) return;
|
|
2182
|
+
if (isHookNotificationMethod(notification.method)) {
|
|
2183
|
+
if (!this.isHookNotificationForCurrentThread(params)) return;
|
|
2184
|
+
} else if (notification.method === "guardianWarning") {
|
|
2185
|
+
if (readCodexNotificationThreadId(params) !== this.threadId) return;
|
|
2186
|
+
} else if (!this.isNotificationForTurn(params)) return;
|
|
2187
|
+
this.nativeToolLifecycleProjector.handleNotification(notification);
|
|
2188
|
+
switch (notification.method) {
|
|
2189
|
+
case "item/agentMessage/delta":
|
|
2190
|
+
await this.assistantProjection.handleAssistantDelta(params);
|
|
2191
|
+
break;
|
|
2192
|
+
case "item/reasoning/summaryTextDelta":
|
|
2193
|
+
case "item/reasoning/textDelta":
|
|
2194
|
+
await this.reasoningProjection.handleReasoningDelta(notification.method, params);
|
|
2195
|
+
break;
|
|
2196
|
+
case "item/plan/delta":
|
|
2197
|
+
this.reasoningProjection.handlePlanDelta(params);
|
|
2198
|
+
break;
|
|
2199
|
+
case "turn/plan/updated":
|
|
2200
|
+
this.reasoningProjection.handleTurnPlanUpdated(params);
|
|
2201
|
+
break;
|
|
2202
|
+
case "item/started":
|
|
2203
|
+
await this.handleItemStarted(params);
|
|
2204
|
+
break;
|
|
2205
|
+
case "item/completed":
|
|
2206
|
+
await this.handleItemCompleted(params);
|
|
2207
|
+
break;
|
|
2208
|
+
case "item/commandExecution/outputDelta":
|
|
2209
|
+
this.toolProgressProjection.handleOutputDelta(params, "bash");
|
|
2210
|
+
break;
|
|
2211
|
+
case "item/autoApprovalReview/started":
|
|
2212
|
+
case "item/autoApprovalReview/completed":
|
|
2213
|
+
this.eventProjection.handleGuardianReview(notification.method, params);
|
|
2214
|
+
break;
|
|
2215
|
+
case "guardianWarning":
|
|
2216
|
+
this.eventProjection.handleGuardianWarning(params);
|
|
2217
|
+
break;
|
|
2218
|
+
case "hook/started":
|
|
2219
|
+
case "hook/completed":
|
|
2220
|
+
this.eventProjection.handleHook(notification.method, params);
|
|
2221
|
+
break;
|
|
2222
|
+
case "thread/tokenUsage/updated":
|
|
2223
|
+
projectCodexThreadUsageUpdate(params, this.tokenUsage, (usage) => this.tokenUsage = usage, (data) => this.emitAgentEvent({
|
|
2224
|
+
stream: "codex_app_server.usage",
|
|
2225
|
+
data
|
|
2226
|
+
}));
|
|
2227
|
+
break;
|
|
2228
|
+
case "turn/completed":
|
|
2229
|
+
await this.handleTurnCompleted(params);
|
|
2230
|
+
break;
|
|
2231
|
+
case "rawResponse/completed":
|
|
2232
|
+
this.handleRawResponseCompleted(params);
|
|
2233
|
+
break;
|
|
2234
|
+
case "rawResponseItem/completed":
|
|
2235
|
+
await this.handleRawResponseItemCompleted(params);
|
|
2236
|
+
break;
|
|
2237
|
+
case "error":
|
|
2238
|
+
this.responseUsage = void 0;
|
|
2239
|
+
if (params.willRetry === true) break;
|
|
2240
|
+
this.promptError = this.formatCodexErrorMessage(params) ?? "codex app-server error";
|
|
2241
|
+
this.promptErrorSource = "prompt";
|
|
2242
|
+
break;
|
|
2243
|
+
case "thread/compacted":
|
|
2244
|
+
case "turn/started":
|
|
2245
|
+
case "turn/diff/updated":
|
|
2246
|
+
case "item/reasoning/summaryPartAdded":
|
|
2247
|
+
case "item/commandExecution/terminalInteraction":
|
|
2248
|
+
case "item/fileChange/outputDelta":
|
|
2249
|
+
case "item/fileChange/patchUpdated":
|
|
2250
|
+
case "item/mcpToolCall/progress":
|
|
2251
|
+
case "model/rerouted":
|
|
2252
|
+
case "model/verification":
|
|
2253
|
+
case "turn/moderationMetadata":
|
|
2254
|
+
case "model/safetyBuffering/updated": break;
|
|
2255
|
+
default:
|
|
2256
|
+
this.diagnostics.warnUnknownEvent(notification, params);
|
|
2257
|
+
break;
|
|
2258
|
+
}
|
|
2259
|
+
}
|
|
2260
|
+
buildResult(toolTelemetry, options) {
|
|
2261
|
+
this.nativeToolLifecycleProjector.finalizeActive();
|
|
2262
|
+
const assistantTexts = this.assistantProjection.collectAssistantTexts();
|
|
2263
|
+
const commentaryMessages = this.assistantProjection.collectCommentaryMessages();
|
|
2264
|
+
const reasoningText = this.reasoningProjection.reasoningText();
|
|
2265
|
+
const planText = this.reasoningProjection.planText();
|
|
2266
|
+
const projectedUsage = this.aborted ? this.tokenUsage : this.responseUsage ?? this.tokenUsage;
|
|
2267
|
+
const hasAssistantItemText = this.assistantProjection.hasAssistantItemTextForSynthesis();
|
|
2268
|
+
const legacyFailClosed = !this.completedTurn || this.completedTurn.status !== "completed" || hasAssistantItemText;
|
|
2269
|
+
const hasDeliverableAssistantOnCompletedTurn = this.completedTurn?.status === "completed" && assistantTexts.some((text) => text.trim().length > 0);
|
|
2270
|
+
const synthesizedMissingToolResultError = this.toolTranscriptProjection.synthesizeMissingToolResults({
|
|
2271
|
+
synthesize: legacyFailClosed,
|
|
2272
|
+
recordPromptError: legacyFailClosed && !hasDeliverableAssistantOnCompletedTurn && !this.aborted
|
|
2273
|
+
});
|
|
2274
|
+
if (synthesizedMissingToolResultError) {
|
|
2275
|
+
this.synthesizedMissingToolResultError = synthesizedMissingToolResultError;
|
|
2276
|
+
this.promptErrorSource = this.promptErrorSource ?? "prompt";
|
|
2277
|
+
}
|
|
2278
|
+
const assistantMessageOptions = {
|
|
2279
|
+
tokenUsage: projectedUsage,
|
|
2280
|
+
aborted: this.aborted,
|
|
2281
|
+
promptError: this.promptError
|
|
2282
|
+
};
|
|
2283
|
+
const lastAssistant = assistantTexts.length ? this.assistantProjection.createAssistantMessage(assistantTexts.join("\n\n"), assistantMessageOptions) : void 0;
|
|
2284
|
+
const currentAttemptAssistant = this.assistantProjection.createCurrentAttemptAssistantMessage(assistantMessageOptions);
|
|
2285
|
+
const messagesSnapshot = buildCodexMessagesSnapshot({
|
|
2286
|
+
runParams: this.params,
|
|
2287
|
+
turnId: this.turnId,
|
|
2288
|
+
upstreamUserText: this.options.upstreamUserText,
|
|
2289
|
+
reasoningText,
|
|
2290
|
+
planText,
|
|
2291
|
+
commentaryMessages,
|
|
2292
|
+
toolMessages: this.toolTranscriptProjection.transcriptMessages,
|
|
2293
|
+
lastAssistant,
|
|
2294
|
+
createAssistantMirrorMessage: (title, text) => this.assistantProjection.createAssistantMirrorMessage(title, text)
|
|
2295
|
+
});
|
|
2296
|
+
const turnFailed = this.completedTurn?.status === "failed";
|
|
2297
|
+
const promptError = this.promptError ?? this.synthesizedMissingToolResultError ?? (turnFailed ? this.completedTurn?.error?.message ?? "codex app-server turn failed" : null);
|
|
2298
|
+
const agentHarnessResultClassification = classifyAgentHarnessTerminalOutcome({
|
|
2299
|
+
assistantTexts,
|
|
2300
|
+
reasoningText,
|
|
2301
|
+
planText,
|
|
2302
|
+
promptError,
|
|
2303
|
+
turnCompleted: Boolean(this.completedTurn)
|
|
2304
|
+
});
|
|
2305
|
+
const toolMetas = this.toolProgressProjection.toolMetas;
|
|
2306
|
+
const hadPotentialSideEffects = toolTelemetry.didSendViaMessagingTool || (toolTelemetry.successfulCronAdds ?? 0) > 0 || this.generatedMediaProjection.hasGeneratedMedia() || this.toolProgressProjection.hasPotentialSideEffects;
|
|
2307
|
+
return {
|
|
2308
|
+
aborted: this.aborted,
|
|
2309
|
+
externalAbort: false,
|
|
2310
|
+
timedOut: false,
|
|
2311
|
+
idleTimedOut: false,
|
|
2312
|
+
timedOutDuringCompaction: false,
|
|
2313
|
+
timedOutDuringToolExecution: false,
|
|
2314
|
+
promptError,
|
|
2315
|
+
promptErrorSource: promptError ? this.promptErrorSource || "prompt" : null,
|
|
2316
|
+
sessionIdUsed: this.params.sessionId,
|
|
2317
|
+
...agentHarnessResultClassification ? { agentHarnessResultClassification } : {},
|
|
2318
|
+
bootstrapPromptWarningSignaturesSeen: this.params.bootstrapPromptWarningSignaturesSeen,
|
|
2319
|
+
bootstrapPromptWarningSignature: this.params.bootstrapPromptWarningSignature,
|
|
2320
|
+
messagesSnapshot,
|
|
2321
|
+
assistantTexts,
|
|
2322
|
+
toolMetas,
|
|
2323
|
+
lastAssistant,
|
|
2324
|
+
currentAttemptAssistant,
|
|
2325
|
+
...this.toolProgressProjection.lastToolError ? { lastToolError: this.toolProgressProjection.lastToolError } : {},
|
|
2326
|
+
didSendViaMessagingTool: toolTelemetry.didSendViaMessagingTool,
|
|
2327
|
+
didDeliverSourceReplyViaMessageTool: toolTelemetry.didDeliverSourceReplyViaMessageTool === true,
|
|
2328
|
+
messagingToolSentTexts: toolTelemetry.messagingToolSentTexts,
|
|
2329
|
+
messagingToolSentMediaUrls: toolTelemetry.messagingToolSentMediaUrls,
|
|
2330
|
+
messagingToolSentTargets: toolTelemetry.messagingToolSentTargets,
|
|
2331
|
+
messagingToolSourceReplyPayloads: toolTelemetry.messagingToolSourceReplyPayloads ?? [],
|
|
2332
|
+
heartbeatToolResponse: toolTelemetry.heartbeatToolResponse,
|
|
2333
|
+
toolMediaUrls: this.generatedMediaProjection.buildToolMediaUrls(toolTelemetry),
|
|
2334
|
+
hostOwnedToolMediaUrls: this.generatedMediaProjection.buildHostOwnedMediaUrls(toolTelemetry),
|
|
2335
|
+
toolAudioAsVoice: toolTelemetry.toolAudioAsVoice,
|
|
2336
|
+
successfulCronAdds: toolTelemetry.successfulCronAdds,
|
|
2337
|
+
cloudCodeAssistFormatError: false,
|
|
2338
|
+
attemptUsage: projectedUsage,
|
|
2339
|
+
...this.completedCompactionCount > 0 ? { compactionCount: this.completedCompactionCount } : {},
|
|
2340
|
+
replayMetadata: {
|
|
2341
|
+
hadPotentialSideEffects,
|
|
2342
|
+
replaySafe: !hadPotentialSideEffects
|
|
2343
|
+
},
|
|
2344
|
+
itemLifecycle: {
|
|
2345
|
+
startedCount: this.activeItemIds.size + this.completedItemIds.size,
|
|
2346
|
+
completedCount: this.completedItemIds.size,
|
|
2347
|
+
activeCount: this.activeItemIds.size
|
|
2348
|
+
},
|
|
2349
|
+
yieldDetected: options?.yieldDetected || false,
|
|
2350
|
+
didSendDeterministicApprovalPrompt: this.eventProjection.guardianReviewCount > 0 ? false : void 0
|
|
2351
|
+
};
|
|
2352
|
+
}
|
|
2353
|
+
recordDynamicToolCall(params) {
|
|
2354
|
+
this.toolTranscriptProjection.recordDynamicToolCall(params);
|
|
2355
|
+
}
|
|
2356
|
+
recordDynamicToolResult(params) {
|
|
2357
|
+
this.toolProgressProjection.recordDynamicToolResult(params);
|
|
2358
|
+
this.toolTranscriptProjection.recordDynamicToolResult(params);
|
|
2359
|
+
}
|
|
2360
|
+
markTimedOut() {
|
|
2361
|
+
this.aborted = true;
|
|
2362
|
+
this.promptError = "codex app-server attempt timed out";
|
|
2363
|
+
this.promptErrorSource = "prompt";
|
|
2364
|
+
}
|
|
2365
|
+
markAborted() {
|
|
2366
|
+
this.aborted = true;
|
|
2367
|
+
this.responseUsage = void 0;
|
|
2368
|
+
}
|
|
2369
|
+
isCompacting() {
|
|
2370
|
+
return this.activeCompactionItemIds.size > 0;
|
|
2371
|
+
}
|
|
2372
|
+
async handleItemStarted(params) {
|
|
2373
|
+
const item = readItem(params.item);
|
|
2374
|
+
const itemId = item?.id ?? readString$5(params, "itemId");
|
|
2375
|
+
this.assistantProjection.recordItemStarted(item, itemId);
|
|
2376
|
+
if (itemId) this.activeItemIds.add(itemId);
|
|
2377
|
+
this.recordNativeToolOutcome(item);
|
|
2378
|
+
if (item?.type === "contextCompaction" && itemId) {
|
|
2379
|
+
this.activeCompactionItemIds.add(itemId);
|
|
2380
|
+
await runAgentHarnessBeforeCompactionHook({
|
|
2381
|
+
sessionFile: this.params.sessionFile,
|
|
2382
|
+
messages: await this.toolTranscriptProjection.readMirroredSessionMessages(),
|
|
2383
|
+
ctx: {
|
|
2384
|
+
runId: this.params.runId,
|
|
2385
|
+
agentId: this.params.agentId,
|
|
2386
|
+
sessionKey: this.params.sessionKey,
|
|
2387
|
+
sessionId: this.params.sessionId,
|
|
2388
|
+
workspaceDir: this.params.workspaceDir,
|
|
2389
|
+
messageProvider: this.params.messageProvider ?? void 0,
|
|
2390
|
+
trigger: this.params.trigger,
|
|
2391
|
+
channelId: this.params.messageChannel ?? this.params.messageProvider ?? void 0
|
|
2392
|
+
}
|
|
2393
|
+
});
|
|
2394
|
+
this.emitAgentEvent({
|
|
2395
|
+
stream: "compaction",
|
|
2396
|
+
data: {
|
|
2397
|
+
phase: "start",
|
|
2398
|
+
backend: "codex-app-server",
|
|
2399
|
+
threadId: this.threadId,
|
|
2400
|
+
turnId: this.turnId,
|
|
2401
|
+
itemId
|
|
2402
|
+
}
|
|
2403
|
+
});
|
|
2404
|
+
}
|
|
2405
|
+
this.toolProgressProjection.recordToolMeta(item);
|
|
2406
|
+
this.eventProjection.emitStandardItemEvent({
|
|
2407
|
+
phase: "start",
|
|
2408
|
+
item
|
|
2409
|
+
});
|
|
2410
|
+
await this.eventProjection.emitNormalizedToolItemEvent({
|
|
2411
|
+
phase: "start",
|
|
2412
|
+
item
|
|
2413
|
+
});
|
|
2414
|
+
this.toolTranscriptProjection.recordNativeToolCall(item);
|
|
2415
|
+
this.toolProgressProjection.emitToolResultSummary(item);
|
|
2416
|
+
this.emitAgentEvent({
|
|
2417
|
+
stream: "codex_app_server.item",
|
|
2418
|
+
data: {
|
|
2419
|
+
phase: "started",
|
|
2420
|
+
itemId,
|
|
2421
|
+
type: item?.type
|
|
2422
|
+
}
|
|
2423
|
+
});
|
|
2424
|
+
}
|
|
2425
|
+
async handleItemCompleted(params) {
|
|
2426
|
+
const item = readItem(params.item);
|
|
2427
|
+
this.diagnostics.warnUnknownItemStatus(item);
|
|
2428
|
+
this.recordNativeToolOutcome(item);
|
|
2429
|
+
this.clearTerminalPresentationForNativeItem(item);
|
|
2430
|
+
const itemId = item?.id ?? readString$5(params, "itemId");
|
|
2431
|
+
if (itemId) {
|
|
2432
|
+
this.activeItemIds.delete(itemId);
|
|
2433
|
+
this.completedItemIds.add(itemId);
|
|
2434
|
+
}
|
|
2435
|
+
this.assistantProjection.recordItemCompleted(item, itemId, this.activeItemIds);
|
|
2436
|
+
this.reasoningProjection.recordItem(item);
|
|
2437
|
+
this.generatedMediaProjection.recordNative(item);
|
|
2438
|
+
if (item?.type === "contextCompaction" && itemId) {
|
|
2439
|
+
this.activeCompactionItemIds.delete(itemId);
|
|
2440
|
+
this.completedCompactionCount += 1;
|
|
2441
|
+
this.options.onContextCompacted?.();
|
|
2442
|
+
await runAgentHarnessAfterCompactionHook({
|
|
2443
|
+
sessionFile: this.params.sessionFile,
|
|
2444
|
+
messages: await this.toolTranscriptProjection.readMirroredSessionMessages(),
|
|
2445
|
+
compactedCount: -1,
|
|
2446
|
+
ctx: {
|
|
2447
|
+
runId: this.params.runId,
|
|
2448
|
+
agentId: this.params.agentId,
|
|
2449
|
+
sessionKey: this.params.sessionKey,
|
|
2450
|
+
sessionId: this.params.sessionId,
|
|
2451
|
+
workspaceDir: this.params.workspaceDir,
|
|
2452
|
+
messageProvider: this.params.messageProvider ?? void 0,
|
|
2453
|
+
trigger: this.params.trigger,
|
|
2454
|
+
channelId: this.params.messageChannel ?? this.params.messageProvider ?? void 0
|
|
2455
|
+
}
|
|
2456
|
+
});
|
|
2457
|
+
this.emitAgentEvent({
|
|
2458
|
+
stream: "compaction",
|
|
2459
|
+
data: {
|
|
2460
|
+
phase: "end",
|
|
2461
|
+
backend: "codex-app-server",
|
|
2462
|
+
completed: true,
|
|
2463
|
+
threadId: this.threadId,
|
|
2464
|
+
turnId: this.turnId,
|
|
2465
|
+
itemId
|
|
2466
|
+
}
|
|
2467
|
+
});
|
|
2468
|
+
}
|
|
2469
|
+
this.toolProgressProjection.recordToolMeta(item);
|
|
2470
|
+
this.toolProgressProjection.rememberCommandAggregateOutputEcho(item);
|
|
2471
|
+
this.eventProjection.emitStandardItemEvent({
|
|
2472
|
+
phase: "end",
|
|
2473
|
+
item
|
|
2474
|
+
});
|
|
2475
|
+
await this.eventProjection.emitNormalizedToolItemEvent({
|
|
2476
|
+
phase: "result",
|
|
2477
|
+
item
|
|
2478
|
+
});
|
|
2479
|
+
this.toolTranscriptProjection.recordNativeToolCall(item);
|
|
2480
|
+
this.toolTranscriptProjection.recordNativeToolResult(item);
|
|
2481
|
+
this.toolProgressProjection.emitToolResultSummary(item);
|
|
2482
|
+
this.toolProgressProjection.emitToolResultOutput(item);
|
|
2483
|
+
this.emitAgentEvent({
|
|
2484
|
+
stream: "codex_app_server.item",
|
|
2485
|
+
data: {
|
|
2486
|
+
phase: "completed",
|
|
2487
|
+
itemId,
|
|
2488
|
+
type: item?.type
|
|
2489
|
+
}
|
|
2490
|
+
});
|
|
2491
|
+
}
|
|
2492
|
+
handleRawResponseCompleted(params) {
|
|
2493
|
+
const usage = isJsonObject(params.usage) ? params.usage : void 0;
|
|
2494
|
+
this.responseUsage = usage ? normalizeCodexResponseTokenUsage(usage) : void 0;
|
|
2495
|
+
}
|
|
2496
|
+
async handleTurnCompleted(params) {
|
|
2497
|
+
const turn = readCodexTurn(params.turn);
|
|
2498
|
+
if (!turn || turn.id !== this.turnId) return;
|
|
2499
|
+
this.completedTurn = turn;
|
|
2500
|
+
if (turn.status !== "completed") this.responseUsage = void 0;
|
|
2501
|
+
if (turn.status === "failed") {
|
|
2502
|
+
const usageLimitMessage = formatCodexUsageLimitErrorMessage({
|
|
2503
|
+
message: turn.error?.message,
|
|
2504
|
+
codexErrorInfo: turn.error?.codexErrorInfo,
|
|
2505
|
+
rateLimits: this.options.readRecentRateLimits?.()
|
|
2506
|
+
});
|
|
2507
|
+
this.promptError = usageLimitMessage ? createCodexUsageLimitPromptError(usageLimitMessage) : turn.error?.message ?? "codex app-server turn failed";
|
|
2508
|
+
this.promptErrorSource = "prompt";
|
|
2509
|
+
}
|
|
2510
|
+
const turnItems = turn.items ?? [];
|
|
2511
|
+
for (let index = turnItems.length - 1; index >= 0; index -= 1) {
|
|
2512
|
+
const item = turnItems[index];
|
|
2513
|
+
if (!item || !this.isCurrentTurnSnapshotItem(item)) continue;
|
|
2514
|
+
if (item?.type === "dynamicToolCall") break;
|
|
2515
|
+
if (shouldClearTerminalPresentationForNativeItem(item)) {
|
|
2516
|
+
this.clearTerminalPresentationForNativeItem(item);
|
|
2517
|
+
break;
|
|
2518
|
+
}
|
|
2519
|
+
}
|
|
2520
|
+
for (const item of turnItems) {
|
|
2521
|
+
this.diagnostics.warnUnknownItemStatus(item);
|
|
2522
|
+
this.assistantProjection.recordSnapshotItem(item);
|
|
2523
|
+
this.reasoningProjection.recordItem(item);
|
|
2524
|
+
this.generatedMediaProjection.recordNative(item);
|
|
2525
|
+
this.toolProgressProjection.recordToolMeta(item);
|
|
2526
|
+
this.toolProgressProjection.rememberCommandAggregateOutputEcho(item);
|
|
2527
|
+
await this.emitSnapshotOnlyNativeToolProgress(item);
|
|
2528
|
+
this.toolTranscriptProjection.recordNativeToolCall(item);
|
|
2529
|
+
this.toolTranscriptProjection.recordNativeToolResult(item);
|
|
2530
|
+
this.toolTranscriptProjection.emitAfterToolCallObservation(item);
|
|
2531
|
+
this.toolProgressProjection.emitToolResultSummary(item);
|
|
2532
|
+
this.toolProgressProjection.emitToolResultOutput(item);
|
|
2533
|
+
}
|
|
2534
|
+
this.assistantProjection.finalizeAnswerCandidate(turn);
|
|
2535
|
+
this.activeCompactionItemIds.clear();
|
|
2536
|
+
await this.reasoningProjection.maybeEndReasoning();
|
|
2537
|
+
}
|
|
2538
|
+
async emitSnapshotOnlyNativeToolProgress(item) {
|
|
2539
|
+
if (!shouldSynthesizeToolProgressForItem(item) || !this.isCurrentTurnSnapshotItem(item) || this.completedItemIds.has(item.id) || itemStatus(item) === "running") return;
|
|
2540
|
+
if (!this.activeItemIds.has(item.id)) {
|
|
2541
|
+
this.eventProjection.emitStandardItemEvent({
|
|
2542
|
+
phase: "start",
|
|
2543
|
+
item
|
|
2544
|
+
});
|
|
2545
|
+
await this.eventProjection.emitNormalizedToolItemEvent({
|
|
2546
|
+
phase: "start",
|
|
2547
|
+
item
|
|
2548
|
+
});
|
|
2549
|
+
}
|
|
2550
|
+
this.activeItemIds.delete(item.id);
|
|
2551
|
+
this.eventProjection.emitStandardItemEvent({
|
|
2552
|
+
phase: "end",
|
|
2553
|
+
item
|
|
2554
|
+
});
|
|
2555
|
+
await this.eventProjection.emitNormalizedToolItemEvent({
|
|
2556
|
+
phase: "result",
|
|
2557
|
+
item
|
|
2558
|
+
});
|
|
2559
|
+
this.completedItemIds.add(item.id);
|
|
2560
|
+
}
|
|
2561
|
+
isCurrentTurnSnapshotItem(item) {
|
|
2562
|
+
const itemTurnId = readItemString(item, "turnId");
|
|
2563
|
+
return itemTurnId === void 0 || itemTurnId === this.turnId;
|
|
2564
|
+
}
|
|
2565
|
+
async handleRawResponseItemCompleted(params) {
|
|
2566
|
+
const item = isJsonObject(params.item) ? params.item : void 0;
|
|
2567
|
+
if (!item) return;
|
|
2568
|
+
this.assistantProjection.handleRawResponseItemCompleted(item, this.activeItemIds);
|
|
2569
|
+
await this.generatedMediaProjection.recordRaw(item);
|
|
2570
|
+
}
|
|
2571
|
+
clearTerminalPresentationForNativeItem(item) {
|
|
2572
|
+
if (!item || this.terminalPresentationClearedItemIds.has(item.id) || !shouldClearTerminalPresentationForNativeItem(item)) return;
|
|
2573
|
+
const toolCallOrdinal = this.nativeToolOutcomeOrdinals.get(item.id);
|
|
2574
|
+
this.terminalPresentationClearedItemIds.add(item.id);
|
|
2575
|
+
this.params.onToolOutcome?.({
|
|
2576
|
+
toolName: itemName(item) ?? item.type,
|
|
2577
|
+
argsHash: "",
|
|
2578
|
+
resultHash: "",
|
|
2579
|
+
...toolCallOrdinal !== void 0 ? { toolCallOrdinal } : {},
|
|
2580
|
+
terminalPresentation: void 0,
|
|
2581
|
+
presentationOnly: true
|
|
2582
|
+
});
|
|
2583
|
+
}
|
|
2584
|
+
formatCodexErrorMessage(params) {
|
|
2585
|
+
const error = isJsonObject(params.error) ? params.error : void 0;
|
|
2586
|
+
const usageLimitMessage = formatCodexUsageLimitErrorMessage({
|
|
2587
|
+
message: error ? readString$5(error, "message") : void 0,
|
|
2588
|
+
codexErrorInfo: error?.codexErrorInfo,
|
|
2589
|
+
rateLimits: this.options.readRecentRateLimits?.()
|
|
2590
|
+
});
|
|
2591
|
+
return usageLimitMessage ? createCodexUsageLimitPromptError(usageLimitMessage) : readCodexErrorNotificationMessage(params);
|
|
2592
|
+
}
|
|
2593
|
+
emitAgentEvent(event) {
|
|
2594
|
+
try {
|
|
2595
|
+
emitAgentEvent({
|
|
2596
|
+
runId: this.params.runId,
|
|
2597
|
+
stream: event.stream,
|
|
2598
|
+
data: event.data,
|
|
2599
|
+
...this.params.sessionKey ? { sessionKey: this.params.sessionKey } : {}
|
|
2600
|
+
});
|
|
2601
|
+
} catch (error) {
|
|
2602
|
+
embeddedAgentLog.debug("codex app-server global agent event emit failed", { error });
|
|
2603
|
+
}
|
|
2604
|
+
try {
|
|
2605
|
+
const maybePromise = this.params.onAgentEvent?.(event);
|
|
2606
|
+
Promise.resolve(maybePromise).catch((error) => {
|
|
2607
|
+
embeddedAgentLog.debug("codex app-server agent event handler rejected", { error });
|
|
2608
|
+
});
|
|
2609
|
+
} catch (error) {
|
|
2610
|
+
embeddedAgentLog.debug("codex app-server agent event handler threw", { error });
|
|
2611
|
+
}
|
|
2612
|
+
}
|
|
2613
|
+
isNotificationForTurn(params) {
|
|
2614
|
+
const threadId = readCodexNotificationThreadId(params);
|
|
2615
|
+
const turnId = readCodexNotificationTurnId(params);
|
|
2616
|
+
return threadId === this.threadId && turnId === this.turnId;
|
|
2617
|
+
}
|
|
2618
|
+
isHookNotificationForCurrentThread(params) {
|
|
2619
|
+
const threadId = readString$5(params, "threadId");
|
|
2620
|
+
const turnId = params.turnId;
|
|
2621
|
+
return threadId === this.threadId && (turnId === this.turnId || turnId === null);
|
|
2622
|
+
}
|
|
2623
|
+
};
|
|
2624
|
+
function isHookNotificationMethod(method) {
|
|
2625
|
+
return method === "hook/started" || method === "hook/completed";
|
|
2626
|
+
}
|
|
2627
|
+
//#endregion
|
|
2628
|
+
//#region extensions/codex/src/app-server/user-input-bridge.ts
|
|
2629
|
+
/** Bridges Codex request_user_input calls to gateway questions and secret text prompts. */
|
|
2630
|
+
const DEFAULT_USER_INPUT_TIMEOUT_MS = 15 * 6e4;
|
|
198
2631
|
/** Creates a per-turn bridge for pending Codex user-input requests. */
|
|
199
2632
|
function createCodexUserInputBridge(params) {
|
|
200
|
-
let
|
|
201
|
-
|
|
202
|
-
|
|
2633
|
+
let sensitiveInput;
|
|
2634
|
+
let pendingGateway;
|
|
2635
|
+
const gatewayCall = params.gatewayCall ?? callGatewayTool;
|
|
2636
|
+
const resolveSecret = (value) => {
|
|
2637
|
+
const current = sensitiveInput;
|
|
203
2638
|
if (!current) return;
|
|
204
|
-
|
|
2639
|
+
sensitiveInput = void 0;
|
|
205
2640
|
current.cleanup();
|
|
206
2641
|
current.resolve(value);
|
|
207
2642
|
};
|
|
208
|
-
const
|
|
209
|
-
if (
|
|
210
|
-
|
|
2643
|
+
const resolveSecretIfCurrent = (current, value) => {
|
|
2644
|
+
if (sensitiveInput !== current) return false;
|
|
2645
|
+
resolveSecret(value);
|
|
211
2646
|
return true;
|
|
212
2647
|
};
|
|
2648
|
+
const cancelGateway = () => {
|
|
2649
|
+
pendingGateway?.abort.abort(/* @__PURE__ */ new Error("Codex user input request cancelled"));
|
|
2650
|
+
};
|
|
213
2651
|
return {
|
|
214
2652
|
async handleRequest(request) {
|
|
215
2653
|
const requestParams = readUserInputParams(request.params);
|
|
216
2654
|
if (!requestParams) return;
|
|
217
2655
|
if (requestParams.threadId !== params.threadId || requestParams.turnId !== params.turnId) return;
|
|
218
2656
|
if (requestParams.questions.length === 0) return emptyUserInputResponse();
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
2657
|
+
resolveSecret(emptyUserInputResponse());
|
|
2658
|
+
cancelGateway();
|
|
2659
|
+
if (requestParams.questions.some((question) => question.isSecret)) return new Promise((resolve) => {
|
|
2660
|
+
const abortListener = () => resolveSecret(emptyUserInputResponse());
|
|
222
2661
|
const cleanup = () => params.signal?.removeEventListener("abort", abortListener);
|
|
223
|
-
|
|
2662
|
+
sensitiveInput = {
|
|
224
2663
|
requestId: request.id,
|
|
225
2664
|
threadId: requestParams.threadId,
|
|
226
|
-
turnId: requestParams.turnId,
|
|
227
|
-
itemId: requestParams.itemId,
|
|
228
2665
|
questions: requestParams.questions,
|
|
229
2666
|
claimed: false,
|
|
230
2667
|
resolve,
|
|
@@ -232,37 +2669,68 @@ function createCodexUserInputBridge(params) {
|
|
|
232
2669
|
};
|
|
233
2670
|
params.signal?.addEventListener("abort", abortListener, { once: true });
|
|
234
2671
|
if (params.signal?.aborted) {
|
|
235
|
-
|
|
2672
|
+
resolveSecret(emptyUserInputResponse());
|
|
236
2673
|
return;
|
|
237
2674
|
}
|
|
238
|
-
|
|
239
|
-
|
|
2675
|
+
deliverAgentHarnessUserInputPrompt(params.paramsForRun, requestParams.questions, {
|
|
2676
|
+
formatText: formatCodexDisplayText,
|
|
2677
|
+
intro: "Codex needs input:"
|
|
2678
|
+
}).catch((error) => {
|
|
2679
|
+
embeddedAgentLog.warn("failed to deliver secret codex user input prompt", { error });
|
|
240
2680
|
});
|
|
241
2681
|
});
|
|
2682
|
+
const abort = new AbortController();
|
|
2683
|
+
const abortFromRun = () => abort.abort(params.signal?.reason);
|
|
2684
|
+
params.signal?.addEventListener("abort", abortFromRun, { once: true });
|
|
2685
|
+
if (params.signal?.aborted) abortFromRun();
|
|
2686
|
+
pendingGateway = {
|
|
2687
|
+
requestId: request.id,
|
|
2688
|
+
threadId: requestParams.threadId,
|
|
2689
|
+
abort
|
|
2690
|
+
};
|
|
2691
|
+
try {
|
|
2692
|
+
const result = await runAgentHarnessGatewayQuestion({
|
|
2693
|
+
questions: requestParams.questions,
|
|
2694
|
+
sessionKey: params.paramsForRun.sessionKey ?? params.paramsForRun.sessionId,
|
|
2695
|
+
agentId: params.paramsForRun.agentId,
|
|
2696
|
+
timeoutMs: requestParams.autoResolutionMs ?? params.paramsForRun.timeoutMs ?? DEFAULT_USER_INPUT_TIMEOUT_MS,
|
|
2697
|
+
gatewayCall,
|
|
2698
|
+
delivery: params.paramsForRun,
|
|
2699
|
+
promptOptions: {
|
|
2700
|
+
formatText: formatCodexDisplayText,
|
|
2701
|
+
intro: "Codex needs input:"
|
|
2702
|
+
},
|
|
2703
|
+
signal: abort.signal
|
|
2704
|
+
});
|
|
2705
|
+
return result.status === "answered" ? gatewayAnswersToCodexResponse(result.answers.answers) : emptyUserInputResponse();
|
|
2706
|
+
} catch (error) {
|
|
2707
|
+
embeddedAgentLog.warn("failed to bridge codex user input through gateway", { error });
|
|
2708
|
+
return emptyUserInputResponse();
|
|
2709
|
+
} finally {
|
|
2710
|
+
params.signal?.removeEventListener("abort", abortFromRun);
|
|
2711
|
+
if (pendingGateway?.abort === abort) pendingGateway = void 0;
|
|
2712
|
+
}
|
|
242
2713
|
},
|
|
243
2714
|
claimPendingRequest() {
|
|
244
|
-
const current =
|
|
2715
|
+
const current = sensitiveInput;
|
|
245
2716
|
if (!current || current.claimed) return;
|
|
246
2717
|
current.claimed = true;
|
|
247
2718
|
return {
|
|
248
|
-
answer: (text) =>
|
|
249
|
-
cancel: () =>
|
|
2719
|
+
answer: (text) => resolveSecretIfCurrent(current, buildUserInputResponse(current.questions, text)),
|
|
2720
|
+
cancel: () => resolveSecretIfCurrent(current, emptyUserInputResponse())
|
|
250
2721
|
};
|
|
251
2722
|
},
|
|
252
|
-
handleQueuedMessage(text) {
|
|
253
|
-
const current = pending;
|
|
254
|
-
if (!current || current.claimed) return false;
|
|
255
|
-
resolvePending(buildUserInputResponse(current.questions, text));
|
|
256
|
-
return true;
|
|
257
|
-
},
|
|
258
2723
|
handleNotification(notification) {
|
|
259
|
-
if (notification.method !== "serverRequest/resolved"
|
|
2724
|
+
if (notification.method !== "serverRequest/resolved") return;
|
|
260
2725
|
const notificationParams = isJsonObject(notification.params) ? notification.params : void 0;
|
|
261
2726
|
const requestId = notificationParams ? readRequestId(notificationParams) : void 0;
|
|
262
|
-
if (notificationParams
|
|
2727
|
+
if (!notificationParams || requestId === void 0) return;
|
|
2728
|
+
if (sensitiveInput && readString$3(notificationParams, "threadId") === sensitiveInput.threadId && String(requestId) === String(sensitiveInput.requestId)) resolveSecret(emptyUserInputResponse());
|
|
2729
|
+
if (pendingGateway && readString$3(notificationParams, "threadId") === pendingGateway.threadId && String(requestId) === String(pendingGateway.requestId)) pendingGateway.abort.abort(/* @__PURE__ */ new Error("Codex server request resolved"));
|
|
263
2730
|
},
|
|
264
2731
|
cancelPending() {
|
|
265
|
-
|
|
2732
|
+
resolveSecret(emptyUserInputResponse());
|
|
2733
|
+
cancelGateway();
|
|
266
2734
|
}
|
|
267
2735
|
};
|
|
268
2736
|
}
|
|
@@ -277,7 +2745,12 @@ function readUserInputParams(value) {
|
|
|
277
2745
|
threadId,
|
|
278
2746
|
turnId,
|
|
279
2747
|
itemId,
|
|
280
|
-
questions: questionsRaw.map(
|
|
2748
|
+
questions: questionsRaw.map((rawQuestion) => {
|
|
2749
|
+
const question = readQuestion(rawQuestion);
|
|
2750
|
+
if (question && isJsonObject(rawQuestion) && rawQuestion.multiSelect === true) question.multiSelect = true;
|
|
2751
|
+
return question;
|
|
2752
|
+
}).filter((question) => Boolean(question)),
|
|
2753
|
+
autoResolutionMs: typeof value.autoResolutionMs === "number" && value.autoResolutionMs > 0 ? value.autoResolutionMs : void 0
|
|
281
2754
|
};
|
|
282
2755
|
}
|
|
283
2756
|
function readQuestion(value) {
|
|
@@ -309,15 +2782,12 @@ function readOption(value) {
|
|
|
309
2782
|
description
|
|
310
2783
|
} : void 0;
|
|
311
2784
|
}
|
|
312
|
-
async function deliverUserInputPrompt(params, questions) {
|
|
313
|
-
await deliverAgentHarnessUserInputPrompt(params, questions, {
|
|
314
|
-
formatText: formatCodexDisplayText,
|
|
315
|
-
intro: "Codex needs input:"
|
|
316
|
-
});
|
|
317
|
-
}
|
|
318
2785
|
function buildUserInputResponse(questions, inputText) {
|
|
319
2786
|
return buildAgentHarnessUserInputAnswers(questions, inputText);
|
|
320
2787
|
}
|
|
2788
|
+
function gatewayAnswersToCodexResponse(answers) {
|
|
2789
|
+
return { answers: Object.fromEntries(Object.entries(answers).map(([questionId, values]) => [questionId, { answers: values }])) };
|
|
2790
|
+
}
|
|
321
2791
|
function emptyUserInputResponse() {
|
|
322
2792
|
return emptyAgentHarnessUserInputAnswers();
|
|
323
2793
|
}
|
|
@@ -421,11 +2891,28 @@ async function activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notif
|
|
|
421
2891
|
const handle = {
|
|
422
2892
|
kind: "embedded",
|
|
423
2893
|
runId: params.runId,
|
|
424
|
-
queueMessage: async (text, optionsLocal) =>
|
|
2894
|
+
queueMessage: async (text, optionsLocal) => {
|
|
2895
|
+
const isInboundUserMessage = optionsLocal?.isInboundUserMessage === true;
|
|
2896
|
+
if (isInboundUserMessage && !optionsLocal?.images?.length) {
|
|
2897
|
+
if (await claimPendingAgentQuestionAnswer({
|
|
2898
|
+
sessionKey: params.sessionKey ?? params.sessionId,
|
|
2899
|
+
text
|
|
2900
|
+
})) return;
|
|
2901
|
+
} else if (isInboundUserMessage) try {
|
|
2902
|
+
await cancelPendingAgentQuestionForSession({
|
|
2903
|
+
sessionKey: params.sessionKey ?? params.sessionId,
|
|
2904
|
+
resolvedBy: "image-reply"
|
|
2905
|
+
});
|
|
2906
|
+
} catch (error) {
|
|
2907
|
+
embeddedAgentLog.warn("failed to cancel codex gateway question before image steering", { error });
|
|
2908
|
+
}
|
|
2909
|
+
await activeSteeringQueue.queue(text, optionsLocal);
|
|
2910
|
+
},
|
|
425
2911
|
isStreaming: () => !state.completed && !runAbortController.signal.aborted,
|
|
426
2912
|
isStopped: () => state.completed || state.timedOut || runAbortController.signal.aborted,
|
|
427
2913
|
isAbortable: () => !terminalState.terminalOutcomeFrozen || terminalState.sharedAbortAllowedAfterTerminalOutcome,
|
|
428
2914
|
isCompacting: () => projectorRef.current?.isCompacting() ?? false,
|
|
2915
|
+
supportsTranscriptCommitWait: true,
|
|
429
2916
|
supportsQueueMessageImages: true,
|
|
430
2917
|
sourceReplyDeliveryMode: params.sourceReplyDeliveryMode,
|
|
431
2918
|
cancel: () => abortExplicitly("cancelled"),
|
|
@@ -488,7 +2975,7 @@ async function activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notif
|
|
|
488
2975
|
async function cleanupCodexAttempt(resources, turnRuntime, lifecycle, requestRuntime, activeTurn) {
|
|
489
2976
|
const { prompt, state: resourceState, trajectoryRecorder, releaseCurrentRoute, releaseSharedClientLeaseAndRetireOneShotClient, releaseSandboxExecEnvironment } = resources;
|
|
490
2977
|
const { connection } = prompt.context.runtime;
|
|
491
|
-
const { params, options, runAbortController } = connection;
|
|
2978
|
+
const { params, options, runAbortController, terminalState, bindingStore, bindingIdentity } = connection;
|
|
492
2979
|
const { state, steeringQueueRef, userInputBridgeRef, turnWatches } = turnRuntime;
|
|
493
2980
|
const { maybeEmitFastModeAutoResetBestEffort, emitLifecycleTerminal, buildLifecycleTerminalMeta } = lifecycle;
|
|
494
2981
|
const { codexModelCallDiagnostics } = requestRuntime;
|
|
@@ -518,10 +3005,17 @@ async function cleanupCodexAttempt(resources, turnRuntime, lifecycle, requestRun
|
|
|
518
3005
|
cleanup: async () => trajectoryRecorder?.flush()
|
|
519
3006
|
});
|
|
520
3007
|
if (!state.timedOut && !runAbortController.signal.aborted) await steeringQueueRef.current?.flushPending();
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
3008
|
+
const retainLiveIncognitoThread = terminalState.turnSucceeded && isIncognitoSessionKey(params.sessionKey);
|
|
3009
|
+
const bindingReleased = isIncognitoSessionKey(params.sessionKey) && !retainLiveIncognitoThread ? await bindingStore.mutate(bindingIdentity, {
|
|
3010
|
+
kind: "clear",
|
|
3011
|
+
threadId: resourceState.thread.threadId
|
|
3012
|
+
}) : true;
|
|
3013
|
+
if (!state.timedOut && !retainLiveIncognitoThread) {
|
|
3014
|
+
if (bindingReleased) await unsubscribeCodexThreadBestEffort(resourceState.client, {
|
|
3015
|
+
threadId: resourceState.thread.threadId,
|
|
3016
|
+
timeoutMs: CODEX_APP_SERVER_UNSUBSCRIBE_TIMEOUT_MS
|
|
3017
|
+
});
|
|
3018
|
+
}
|
|
525
3019
|
userInputBridgeRef.current?.cancelPending();
|
|
526
3020
|
turnWatches.clearAllTimers();
|
|
527
3021
|
releaseCurrentRoute();
|
|
@@ -782,19 +3276,11 @@ function readCodexAppServerRolloutTokenSnapshotLine(line) {
|
|
|
782
3276
|
return;
|
|
783
3277
|
}
|
|
784
3278
|
}
|
|
785
|
-
function toNonNegativeInt(value) {
|
|
786
|
-
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) return;
|
|
787
|
-
return Math.floor(value);
|
|
788
|
-
}
|
|
789
3279
|
function readCompactionConfig(config) {
|
|
790
3280
|
return isJsonObject(config?.agents?.defaults?.compaction) ? config.agents.defaults.compaction : void 0;
|
|
791
3281
|
}
|
|
792
|
-
function resolveCodexAppServerNativeThreadReserveTokens(
|
|
793
|
-
|
|
794
|
-
const reserveTokens = toNonNegativeInt(compaction?.reserveTokens);
|
|
795
|
-
const reserveTokensFloor = toNonNegativeInt(compaction?.reserveTokensFloor);
|
|
796
|
-
if (reserveTokens !== void 0) return Math.max(reserveTokens, reserveTokensFloor ?? CODEX_APP_SERVER_NATIVE_THREAD_DEFAULT_RESERVE_TOKENS);
|
|
797
|
-
return reserveTokensFloor ?? CODEX_APP_SERVER_NATIVE_THREAD_DEFAULT_RESERVE_TOKENS;
|
|
3282
|
+
function resolveCodexAppServerNativeThreadReserveTokens(_config) {
|
|
3283
|
+
return CODEX_APP_SERVER_NATIVE_THREAD_DEFAULT_RESERVE_TOKENS;
|
|
798
3284
|
}
|
|
799
3285
|
function resolveCodexAppServerNativeThreadTokenFuse(params) {
|
|
800
3286
|
const projectedTurnTokens = typeof params.projectedTurnTokens === "number" && Number.isFinite(params.projectedTurnTokens) && params.projectedTurnTokens > 0 ? Math.floor(params.projectedTurnTokens) : 0;
|
|
@@ -890,6 +3376,14 @@ async function rotateOversizedCodexAppServerStartupBinding(params) {
|
|
|
890
3376
|
}
|
|
891
3377
|
//#endregion
|
|
892
3378
|
//#region extensions/codex/src/app-server/run-attempt-connection.ts
|
|
3379
|
+
function applyStoredBindingPermissions(params) {
|
|
3380
|
+
if (params.execPolicyTouched || params.binding?.connectionScope === "supervision") return params.appServer;
|
|
3381
|
+
return {
|
|
3382
|
+
...params.appServer,
|
|
3383
|
+
approvalPolicy: params.binding?.approvalPolicy ?? params.appServer.approvalPolicy,
|
|
3384
|
+
sandbox: params.binding?.sandbox ?? params.appServer.sandbox
|
|
3385
|
+
};
|
|
3386
|
+
}
|
|
893
3387
|
async function prepareCodexAttemptConnection({ params, options }) {
|
|
894
3388
|
const attemptStartedAt = Date.now();
|
|
895
3389
|
const profilerEnabled = isCodexAppServerProfilerEnabled(params.config);
|
|
@@ -947,23 +3441,27 @@ async function prepareCodexAttemptConnection({ params, options }) {
|
|
|
947
3441
|
bindingStore,
|
|
948
3442
|
identity: bindingIdentity,
|
|
949
3443
|
config: params.config
|
|
950
|
-
})) throw
|
|
3444
|
+
})) throw createCodexSessionGenerationSupersededError(bindingIdentity.sessionId);
|
|
951
3445
|
startupBinding = await bindingStore.read(bindingIdentity);
|
|
952
3446
|
}
|
|
953
3447
|
preDynamicStartupStages.mark("read-binding");
|
|
954
3448
|
const usesSupervisionConnection = startupBinding?.connectionScope === "supervision";
|
|
955
3449
|
if (usesSupervisionConnection) activeContextEngine = void 0;
|
|
956
3450
|
if (usesSupervisionConnection && pluginConfig.supervision?.enabled !== true) throw new Error("Codex supervision is disabled; refusing to open a native user-home supervised session");
|
|
957
|
-
const resolveRuntimeOptionsForBinding = (selection) =>
|
|
3451
|
+
const resolveRuntimeOptionsForBinding = (selection) => applyStoredBindingPermissions({
|
|
3452
|
+
appServer: resolveCodexBindingAppServerConnection({
|
|
3453
|
+
binding: startupBinding,
|
|
3454
|
+
pluginConfig,
|
|
3455
|
+
execPolicy,
|
|
3456
|
+
modelProvider: selection.modelProvider,
|
|
3457
|
+
model: selection.model,
|
|
3458
|
+
config: params.config,
|
|
3459
|
+
agentDir,
|
|
3460
|
+
openClawSandboxActive: sandbox?.enabled === true
|
|
3461
|
+
}).appServer,
|
|
958
3462
|
binding: startupBinding,
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
modelProvider: selection.modelProvider,
|
|
962
|
-
model: selection.model,
|
|
963
|
-
config: params.config,
|
|
964
|
-
agentDir,
|
|
965
|
-
openClawSandboxActive: sandbox?.enabled === true
|
|
966
|
-
}).appServer;
|
|
3463
|
+
execPolicyTouched: execPolicy.touched
|
|
3464
|
+
});
|
|
967
3465
|
const initialStartupBindingHadInactiveThreadBootstrap = isInactiveThreadBootstrapBinding(startupBinding);
|
|
968
3466
|
const preparedAuthRoute = usesSupervisionConnection ? void 0 : params.runtimePlan?.auth.modelRoute;
|
|
969
3467
|
const startupAuthProfileCandidate = usesSupervisionConnection ? void 0 : preparedAuthRoute ? params.runtimePlan?.auth.forwardedAuthProfileId : params.runtimePlan?.auth.forwardedAuthProfileId ?? params.authProfileId ?? startupBinding?.authProfileId;
|
|
@@ -1039,6 +3537,7 @@ async function prepareCodexAttemptConnection({ params, options }) {
|
|
|
1039
3537
|
preDynamicStartupStages.mark("app-server-policy");
|
|
1040
3538
|
preDynamicStartupStages.mark("native-hook-relay");
|
|
1041
3539
|
const terminalState = {
|
|
3540
|
+
turnSucceeded: false,
|
|
1042
3541
|
explicitCancellationObserved: false,
|
|
1043
3542
|
explicitCancellationReason: void 0,
|
|
1044
3543
|
terminalOutcomeFrozen: false,
|
|
@@ -1100,16 +3599,20 @@ async function prepareCodexAttemptConnection({ params, options }) {
|
|
|
1100
3599
|
startupBinding,
|
|
1101
3600
|
pluginAppServer: appServer
|
|
1102
3601
|
};
|
|
1103
|
-
const resolveRuntimeOptionsForCurrentBinding = (selection) =>
|
|
3602
|
+
const resolveRuntimeOptionsForCurrentBinding = (selection) => applyStoredBindingPermissions({
|
|
3603
|
+
appServer: resolveCodexBindingAppServerConnection({
|
|
3604
|
+
binding: mutable.startupBinding,
|
|
3605
|
+
pluginConfig,
|
|
3606
|
+
execPolicy,
|
|
3607
|
+
modelProvider: selection.modelProvider,
|
|
3608
|
+
model: selection.model,
|
|
3609
|
+
config: params.config,
|
|
3610
|
+
agentDir,
|
|
3611
|
+
openClawSandboxActive: sandbox?.enabled === true
|
|
3612
|
+
}).appServer,
|
|
1104
3613
|
binding: mutable.startupBinding,
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
modelProvider: selection.modelProvider,
|
|
1108
|
-
model: selection.model,
|
|
1109
|
-
config: params.config,
|
|
1110
|
-
agentDir,
|
|
1111
|
-
openClawSandboxActive: sandbox?.enabled === true
|
|
1112
|
-
}).appServer;
|
|
3614
|
+
execPolicyTouched: execPolicy.touched
|
|
3615
|
+
});
|
|
1113
3616
|
return {
|
|
1114
3617
|
params,
|
|
1115
3618
|
options,
|
|
@@ -1169,7 +3672,6 @@ const CODEX_TURN_SCOPED_WORKSPACE_DEVELOPER_CONTEXT_BASENAMES = /* @__PURE__ */
|
|
|
1169
3672
|
"user.md"
|
|
1170
3673
|
]);
|
|
1171
3674
|
const CODEX_WORKSPACE_DEVELOPER_CONTEXT_BASENAMES = /* @__PURE__ */ new Set([...CODEX_INHERITED_WORKSPACE_DEVELOPER_CONTEXT_BASENAMES, ...CODEX_TURN_SCOPED_WORKSPACE_DEVELOPER_CONTEXT_BASENAMES]);
|
|
1172
|
-
const CODEX_HEARTBEAT_CONTEXT_BASENAME = "heartbeat.md";
|
|
1173
3675
|
const CODEX_MEMORY_CONTEXT_BASENAME = "memory.md";
|
|
1174
3676
|
const CODEX_MEMORY_TOOL_NAMES = /* @__PURE__ */ new Set(["memory_search", "memory_get"]);
|
|
1175
3677
|
const CODEX_BOOTSTRAP_CONTEXT_ORDER = /* @__PURE__ */ new Map([
|
|
@@ -1178,8 +3680,7 @@ const CODEX_BOOTSTRAP_CONTEXT_ORDER = /* @__PURE__ */ new Map([
|
|
|
1178
3680
|
["user.md", 30],
|
|
1179
3681
|
["tools.md", 40],
|
|
1180
3682
|
["bootstrap.md", 50],
|
|
1181
|
-
["memory.md", 60]
|
|
1182
|
-
["heartbeat.md", 70]
|
|
3683
|
+
["memory.md", 60]
|
|
1183
3684
|
]);
|
|
1184
3685
|
/** Reads mirrored Codex session history for harness hooks. */
|
|
1185
3686
|
async function readMirroredSessionHistoryMessages(params) {
|
|
@@ -1280,14 +3781,12 @@ async function buildCodexWorkspaceBootstrapContext(params) {
|
|
|
1280
3781
|
});
|
|
1281
3782
|
const developerInstructionFiles = shouldInjectCodexOpenClawPromptContext(params.params) ? selectCodexWorkspaceInheritedDeveloperInstructionFiles(contextFiles) : [];
|
|
1282
3783
|
const turnScopedDeveloperInstructionFiles = shouldInjectCodexOpenClawPromptContext(params.params) ? selectCodexWorkspaceTurnScopedDeveloperInstructionFiles(contextFiles) : [];
|
|
1283
|
-
const heartbeatReferenceFiles = selectCodexWorkspaceHeartbeatReferenceFiles(contextFiles);
|
|
1284
3784
|
return {
|
|
1285
3785
|
bootstrapFiles,
|
|
1286
3786
|
contextFiles,
|
|
1287
3787
|
promptContextFiles,
|
|
1288
3788
|
developerInstructionFiles,
|
|
1289
3789
|
turnScopedDeveloperInstructionFiles,
|
|
1290
|
-
heartbeatReferenceFiles,
|
|
1291
3790
|
memoryReferenceFiles,
|
|
1292
3791
|
memoryToolRoutedBootstrapFiles,
|
|
1293
3792
|
memoryToolNames: [...params.memoryToolNames],
|
|
@@ -1295,15 +3794,15 @@ async function buildCodexWorkspaceBootstrapContext(params) {
|
|
|
1295
3794
|
promptContext: renderCodexWorkspaceBootstrapPromptContext(promptContextFiles),
|
|
1296
3795
|
developerInstructions: renderCodexWorkspaceThreadDeveloperInstructions(developerInstructionFiles),
|
|
1297
3796
|
turnScopedDeveloperInstructions: renderCodexWorkspaceCollaborationDeveloperInstructions(turnScopedDeveloperInstructionFiles),
|
|
1298
|
-
memoryCollaborationInstructions: shouldInjectCodexOpenClawPromptContext(params.params) ? renderCodexWorkspaceMemoryCollaborationInstructions({
|
|
3797
|
+
memoryCollaborationInstructions: shouldInjectCodexOpenClawPromptContext(params.params) ? await renderCodexWorkspaceMemoryCollaborationInstructions({
|
|
1299
3798
|
files: memoryReferenceFiles,
|
|
1300
3799
|
toolNames: params.memoryToolNames,
|
|
1301
3800
|
memoryToolRouted: memoryToolsAvailable,
|
|
1302
3801
|
citationsMode: params.params.config?.memory?.citations,
|
|
1303
3802
|
agentId: params.params.agentId ?? params.sessionAgentId,
|
|
1304
|
-
agentSessionKey: params.sessionKey
|
|
1305
|
-
|
|
1306
|
-
|
|
3803
|
+
agentSessionKey: params.sessionKey,
|
|
3804
|
+
sandboxed: params.sandboxed
|
|
3805
|
+
}) : void 0
|
|
1307
3806
|
};
|
|
1308
3807
|
} catch (error) {
|
|
1309
3808
|
embeddedAgentLog.warn("failed to load codex workspace bootstrap instructions", { error });
|
|
@@ -1425,14 +3924,9 @@ function buildCodexBootstrapInjectionStats(params) {
|
|
|
1425
3924
|
const injected = memoryToolRoutedFile ? void 0 : readCodexIndexedContextFileContent(injectedIndex, pathValue, fileName) ?? readCodexIndexedContextFileContent(developerInstructionIndex, pathValue, fileName);
|
|
1426
3925
|
let injectedChars = memoryToolRoutedFile ? 0 : injected?.length ?? 0;
|
|
1427
3926
|
let truncated = memoryToolRoutedFile ? false : !file.missing && injectedChars < rawChars;
|
|
1428
|
-
if (injected === void 0) {
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
truncated = false;
|
|
1432
|
-
} else if (baseName === CODEX_HEARTBEAT_CONTEXT_BASENAME) {
|
|
1433
|
-
injectedChars = 0;
|
|
1434
|
-
truncated = false;
|
|
1435
|
-
}
|
|
3927
|
+
if (injected === void 0 && CODEX_NATIVE_PROJECT_DOC_BASENAMES.has(baseName)) {
|
|
3928
|
+
injectedChars = rawChars;
|
|
3929
|
+
truncated = false;
|
|
1436
3930
|
}
|
|
1437
3931
|
return {
|
|
1438
3932
|
name: displayName,
|
|
@@ -1556,7 +4050,7 @@ function renderCodexWorkspaceBootstrapPromptContext(contextFiles) {
|
|
|
1556
4050
|
const files = contextFiles;
|
|
1557
4051
|
if (files.length === 0) return;
|
|
1558
4052
|
const lines = [
|
|
1559
|
-
"OpenClaw loaded these user-editable workspace files for the current turn. Codex loads AGENTS.md natively. TOOLS.md is provided as inherited Codex developer instructions. SOUL.md, IDENTITY.md, and USER.md are provided as turn-scoped collaboration instructions so native Codex subagents do not inherit them.
|
|
4053
|
+
"OpenClaw loaded these user-editable workspace files for the current turn. Codex loads AGENTS.md natively. TOOLS.md is provided as inherited Codex developer instructions. SOUL.md, IDENTITY.md, and USER.md are provided as turn-scoped collaboration instructions so native Codex subagents do not inherit them. Those files are not repeated here.",
|
|
1560
4054
|
"",
|
|
1561
4055
|
"# Project Context",
|
|
1562
4056
|
"",
|
|
@@ -1570,7 +4064,7 @@ function selectCodexWorkspacePromptContextFiles(contextFiles, options = {}) {
|
|
|
1570
4064
|
const excludeMemory = options.excludeMemory ?? true;
|
|
1571
4065
|
return contextFiles.filter((file) => {
|
|
1572
4066
|
const baseName = getCodexContextFileBasename(file.path);
|
|
1573
|
-
return baseName && !CODEX_NATIVE_PROJECT_DOC_BASENAMES.has(baseName) && !CODEX_WORKSPACE_DEVELOPER_CONTEXT_BASENAMES.has(baseName) &&
|
|
4067
|
+
return baseName && !CODEX_NATIVE_PROJECT_DOC_BASENAMES.has(baseName) && !CODEX_WORKSPACE_DEVELOPER_CONTEXT_BASENAMES.has(baseName) && (!excludeMemory || !isCodexWorkspaceRootMemoryContextFile({
|
|
1574
4068
|
file,
|
|
1575
4069
|
workspaceDir: options.memoryWorkspaceDir
|
|
1576
4070
|
})) && !isMissingCodexBootstrapContextFile(file);
|
|
@@ -1617,22 +4111,6 @@ function renderCodexWorkspaceDeveloperInstructions(params) {
|
|
|
1617
4111
|
if (wrapperTag) lines.push(`</${wrapperTag}>`);
|
|
1618
4112
|
return lines.join("\n").trim();
|
|
1619
4113
|
}
|
|
1620
|
-
function selectCodexWorkspaceHeartbeatReferenceFiles(contextFiles) {
|
|
1621
|
-
return contextFiles.filter((file) => {
|
|
1622
|
-
return getCodexContextFileBasename(file.path) === CODEX_HEARTBEAT_CONTEXT_BASENAME && !isMissingCodexBootstrapContextFile(file) && file.content.trim().length > 0;
|
|
1623
|
-
}).toSorted(compareCodexContextFiles);
|
|
1624
|
-
}
|
|
1625
|
-
function renderCodexWorkspaceHeartbeatReference(files) {
|
|
1626
|
-
if (files.length === 0) return;
|
|
1627
|
-
const lines = [
|
|
1628
|
-
"## OpenClaw Heartbeat Workspace",
|
|
1629
|
-
"",
|
|
1630
|
-
"HEARTBEAT.md exists in the active agent workspace. Read it before proceeding with this heartbeat, then decide what action is appropriate.",
|
|
1631
|
-
""
|
|
1632
|
-
];
|
|
1633
|
-
for (const file of files) lines.push(`- ${file.path}`);
|
|
1634
|
-
return lines.join("\n").trim();
|
|
1635
|
-
}
|
|
1636
4114
|
function selectCodexWorkspaceMemoryReferenceFiles(params) {
|
|
1637
4115
|
return params.bootstrapFiles.filter((file) => {
|
|
1638
4116
|
return isCodexWorkspaceRootMemoryBootstrapFile({
|
|
@@ -1656,24 +4134,26 @@ function renderCodexWorkspaceMemoryReference(params) {
|
|
|
1656
4134
|
for (const file of params.files) lines.push(`- ${file.path}`);
|
|
1657
4135
|
return lines.join("\n").trim();
|
|
1658
4136
|
}
|
|
1659
|
-
function renderCodexWorkspaceMemoryCollaborationInstructions(params) {
|
|
1660
|
-
const sections = [params.memoryToolRouted ? renderCodexMemoryRecallInstructions({
|
|
4137
|
+
async function renderCodexWorkspaceMemoryCollaborationInstructions(params) {
|
|
4138
|
+
const sections = [params.memoryToolRouted ? await renderCodexMemoryRecallInstructions({
|
|
1661
4139
|
toolNames: params.toolNames,
|
|
1662
4140
|
citationsMode: params.citationsMode,
|
|
1663
4141
|
agentId: params.agentId,
|
|
1664
|
-
agentSessionKey: params.agentSessionKey
|
|
4142
|
+
agentSessionKey: params.agentSessionKey,
|
|
4143
|
+
sandboxed: params.sandboxed
|
|
1665
4144
|
}) : void 0, renderCodexWorkspaceMemoryReference({
|
|
1666
4145
|
files: params.files,
|
|
1667
4146
|
toolNames: params.toolNames
|
|
1668
4147
|
})].filter(isNonEmptyString$1);
|
|
1669
4148
|
return sections.length > 0 ? sections.join("\n\n") : void 0;
|
|
1670
4149
|
}
|
|
1671
|
-
function renderCodexMemoryRecallInstructions(params) {
|
|
1672
|
-
const memoryPrompt =
|
|
4150
|
+
async function renderCodexMemoryRecallInstructions(params) {
|
|
4151
|
+
const memoryPrompt = await prepareMemorySystemPromptAddition({
|
|
1673
4152
|
availableTools: new Set(params.toolNames),
|
|
1674
4153
|
citationsMode: params.citationsMode,
|
|
1675
4154
|
agentId: params.agentId,
|
|
1676
|
-
agentSessionKey: params.agentSessionKey
|
|
4155
|
+
agentSessionKey: params.agentSessionKey,
|
|
4156
|
+
sandboxed: params.sandboxed
|
|
1677
4157
|
});
|
|
1678
4158
|
if (!memoryPrompt) return;
|
|
1679
4159
|
return [memoryPrompt, renderCodexMemoryToolSearchBridge(params.toolNames)].filter(isNonEmptyString$1).join("\n").trim();
|
|
@@ -1870,7 +4350,7 @@ function buildCodexAppServerTimeoutDiagnostics(params) {
|
|
|
1870
4350
|
//#region extensions/codex/src/app-server/run-attempt-context.ts
|
|
1871
4351
|
async function prepareCodexAttemptContext(runtime, attemptTools) {
|
|
1872
4352
|
const { connection, runtimeParams, activeSessionId, activeSessionFile, buildActiveRunAttemptParams, effectiveContextWindowInfo, effectiveContextTokenBudget, effectiveRuntimeProviderId, effectiveRuntimeModelId, hookChannelId } = runtime;
|
|
1873
|
-
const { params, sessionAgentId, contextSessionKey, activeContextEngine, initialStartupBindingHadInactiveThreadBootstrap, sandboxSessionKey, effectiveWorkspace, effectiveCwd, agentDir, usesSupervisionConnection, resolvedWorkspace, initialInactiveThreadBootstrapBindingForcedFreshStart } = connection;
|
|
4353
|
+
const { params, sessionAgentId, contextSessionKey, activeContextEngine, initialStartupBindingHadInactiveThreadBootstrap, sandboxSessionKey, effectiveWorkspace, effectiveCwd, agentDir, usesSupervisionConnection, resolvedWorkspace, initialInactiveThreadBootstrapBindingForcedFreshStart, sandbox } = connection;
|
|
1874
4354
|
const { toolBridge } = attemptTools;
|
|
1875
4355
|
const activeTranscriptTarget = {
|
|
1876
4356
|
agentId: sessionAgentId,
|
|
@@ -1934,7 +4414,8 @@ async function prepareCodexAttemptContext(runtime, attemptTools) {
|
|
|
1934
4414
|
effectiveWorkspace,
|
|
1935
4415
|
sessionKey: contextSessionKey,
|
|
1936
4416
|
sessionAgentId,
|
|
1937
|
-
memoryToolNames: getCodexWorkspaceMemoryToolNames(toolBridge.availableSpecs)
|
|
4417
|
+
memoryToolNames: getCodexWorkspaceMemoryToolNames(toolBridge.availableSpecs),
|
|
4418
|
+
sandboxed: sandbox?.enabled === true
|
|
1938
4419
|
});
|
|
1939
4420
|
const baseDeveloperInstructions = joinPresentSections(buildDeveloperInstructions(runtimeParams, { dynamicTools: toolBridge.availableSpecs }), workspaceBootstrapContext.developerInstructions);
|
|
1940
4421
|
return {
|
|
@@ -1968,7 +4449,7 @@ async function prepareCodexAttemptContext(runtime, attemptTools) {
|
|
|
1968
4449
|
},
|
|
1969
4450
|
codexContextProjectionMaxChars: resolveCodexContextEngineProjectionMaxChars({
|
|
1970
4451
|
contextTokenBudget: effectiveContextTokenBudget,
|
|
1971
|
-
reserveTokens: resolveCodexContextEngineProjectionReserveTokens(
|
|
4452
|
+
reserveTokens: resolveCodexContextEngineProjectionReserveTokens()
|
|
1972
4453
|
})
|
|
1973
4454
|
};
|
|
1974
4455
|
}
|
|
@@ -2183,6 +4664,67 @@ function isInvalidCodexImagePayloadError(message) {
|
|
|
2183
4664
|
return /\b(?:invalid|malformed)\b[\s\S]{0,120}\b(?:image|image url|base64)\b/iu.test(normalizedMessage) || /\b(?:image|image url|base64)\b[\s\S]{0,120}\b(?:invalid|malformed)\b/iu.test(normalizedMessage);
|
|
2184
4665
|
}
|
|
2185
4666
|
//#endregion
|
|
4667
|
+
//#region extensions/codex/src/app-server/settled-turn-context.ts
|
|
4668
|
+
function collectUniqueMessageIdentities(messages) {
|
|
4669
|
+
const identities = /* @__PURE__ */ new Map();
|
|
4670
|
+
for (const [index, message] of messages.entries()) {
|
|
4671
|
+
const identity = readMirrorIdentity(message);
|
|
4672
|
+
if (!identity) continue;
|
|
4673
|
+
if (identities.has(identity)) return;
|
|
4674
|
+
identities.set(identity, index);
|
|
4675
|
+
}
|
|
4676
|
+
return identities;
|
|
4677
|
+
}
|
|
4678
|
+
/** Freezes one complete active transcript branch through the settled tool-result boundary. */
|
|
4679
|
+
function buildCodexSettledTurnFinalizationContext(params) {
|
|
4680
|
+
const boundaryMessage = params.settledMessages.findLast((message) => message.role === "toolResult");
|
|
4681
|
+
const boundaryIdentity = boundaryMessage ? readMirrorIdentity(boundaryMessage) : void 0;
|
|
4682
|
+
if (!boundaryMessage || !boundaryIdentity || !boundaryIdentity.startsWith(`${params.turnId}:tool:`)) return;
|
|
4683
|
+
const settledBoundaryIndex = params.settledMessages.indexOf(boundaryMessage);
|
|
4684
|
+
const requiredIdentities = params.settledMessages.slice(0, settledBoundaryIndex + 1).map(readMirrorIdentity);
|
|
4685
|
+
if (requiredIdentities.length === 0 || requiredIdentities.some((identity) => !identity) || new Set(requiredIdentities).size !== requiredIdentities.length || !requiredIdentities.includes(`${params.turnId}:prompt`)) return;
|
|
4686
|
+
const historyIdentities = collectUniqueMessageIdentities(params.historyMessages);
|
|
4687
|
+
const mirroredIdentities = collectUniqueMessageIdentities(params.mirroredMessages);
|
|
4688
|
+
if (!historyIdentities || !mirroredIdentities) return;
|
|
4689
|
+
const mirroredBoundaryIndex = mirroredIdentities.get(boundaryIdentity);
|
|
4690
|
+
if (mirroredBoundaryIndex === void 0) return;
|
|
4691
|
+
const mirroredThroughBoundary = params.mirroredMessages.slice(0, mirroredBoundaryIndex + 1);
|
|
4692
|
+
if (mirroredThroughBoundary.length !== requiredIdentities.length || mirroredThroughBoundary.some((message, index) => readMirrorIdentity(message) !== requiredIdentities[index])) return;
|
|
4693
|
+
const historyBoundaryIndex = historyIdentities.get(boundaryIdentity);
|
|
4694
|
+
if (historyBoundaryIndex === void 0) return;
|
|
4695
|
+
let previousHistoryIndex = -1;
|
|
4696
|
+
for (const mirroredMessage of mirroredThroughBoundary) {
|
|
4697
|
+
const identity = readMirrorIdentity(mirroredMessage);
|
|
4698
|
+
const historyIndex = identity ? historyIdentities.get(identity) : void 0;
|
|
4699
|
+
const historyMessage = historyIndex === void 0 ? void 0 : params.historyMessages[historyIndex];
|
|
4700
|
+
if (historyIndex === void 0 || historyIndex <= previousHistoryIndex || historyIndex > historyBoundaryIndex || !historyMessage || serializeCodexMirrorSourceEvidence(historyMessage) !== serializeCodexMirrorSourceEvidence(mirroredMessage)) return;
|
|
4701
|
+
previousHistoryIndex = historyIndex;
|
|
4702
|
+
}
|
|
4703
|
+
return {
|
|
4704
|
+
source: "openclaw-transcript",
|
|
4705
|
+
messages: Object.freeze(structuredClone(params.historyMessages.slice(0, historyBoundaryIndex + 1)))
|
|
4706
|
+
};
|
|
4707
|
+
}
|
|
4708
|
+
/** Reads and freezes the current active transcript branch after mirroring has settled. */
|
|
4709
|
+
async function captureCodexSettledTurnFinalizationContext(params) {
|
|
4710
|
+
try {
|
|
4711
|
+
const historyMessages = await readCodexMirroredSessionHistoryMessages(params);
|
|
4712
|
+
if (!historyMessages) return;
|
|
4713
|
+
return buildCodexSettledTurnFinalizationContext({
|
|
4714
|
+
historyMessages,
|
|
4715
|
+
mirroredMessages: params.mirroredMessages,
|
|
4716
|
+
settledMessages: params.settledMessages,
|
|
4717
|
+
turnId: params.turnId
|
|
4718
|
+
});
|
|
4719
|
+
} catch (error) {
|
|
4720
|
+
embeddedAgentLog.warn("codex settled-turn finalization context capture failed", {
|
|
4721
|
+
error: formatErrorMessage(error),
|
|
4722
|
+
turnId: params.turnId
|
|
4723
|
+
});
|
|
4724
|
+
return;
|
|
4725
|
+
}
|
|
4726
|
+
}
|
|
4727
|
+
//#endregion
|
|
2186
4728
|
//#region extensions/codex/src/app-server/trajectory.ts
|
|
2187
4729
|
const SENSITIVE_FIELD_RE = /(?:authorization|cookie|credential|key|password|passwd|secret|token)/iu;
|
|
2188
4730
|
const PRIVATE_PAYLOAD_FIELD_RE = /(?:image|screenshot|attachment|fileData|dataUri)/iu;
|
|
@@ -2476,6 +5018,7 @@ async function finalizeCodexAttempt(resources, turnRuntime, lifecycle, notificat
|
|
|
2476
5018
|
const turnSucceeded = !finalAborted && !effectiveTimedOut && (finalPromptError === null || finalPromptError === void 0) && (completedTurnStatus === "completed" || recoveredTurnWatchTimeout || completedWithoutTerminalNotification);
|
|
2477
5019
|
if (settleCodexSourceReplyFinality(toolBridge.telemetry, turnSucceeded)) result.agentHarnessResultClassification = void 0;
|
|
2478
5020
|
const attemptSucceeded = turnSucceeded && result.agentHarnessResultClassification === void 0;
|
|
5021
|
+
terminalState.turnSucceeded = turnSucceeded;
|
|
2479
5022
|
terminalState.sharedAbortAllowedAfterTerminalOutcome = shouldKeepCodexSharedAbortOpen({
|
|
2480
5023
|
trigger: params.trigger,
|
|
2481
5024
|
result,
|
|
@@ -2495,7 +5038,7 @@ async function finalizeCodexAttempt(resources, turnRuntime, lifecycle, notificat
|
|
|
2495
5038
|
if (modelCallFailureKind) codexModelCallDiagnostics.emitError(finalPromptError ?? "codex app-server attempt interrupted", { failureKind: modelCallFailureKind });
|
|
2496
5039
|
else if (finalPromptError) codexModelCallDiagnostics.emitError(finalPromptError);
|
|
2497
5040
|
else codexModelCallDiagnostics.emitCompleted(result);
|
|
2498
|
-
const
|
|
5041
|
+
const mirrorOutcome = await codexTranscriptMirrorRuntime.mirrorBestEffort({
|
|
2499
5042
|
params,
|
|
2500
5043
|
agentId: sessionAgentId,
|
|
2501
5044
|
notifyUserMessagePersisted,
|
|
@@ -2505,6 +5048,18 @@ async function finalizeCodexAttempt(resources, turnRuntime, lifecycle, notificat
|
|
|
2505
5048
|
threadId: resourceState.thread.threadId,
|
|
2506
5049
|
turnId: activeTurnId
|
|
2507
5050
|
});
|
|
5051
|
+
const { assistantTranscriptOwned, assistantTranscriptIdempotencyKey } = mirrorOutcome;
|
|
5052
|
+
const shouldCaptureSettledTurnFinalizationContext = turnSucceeded && result.assistantTexts.every((text) => !text.trim()) && result.messagesSnapshot.some((message) => message.role === "toolResult");
|
|
5053
|
+
const settledTurnFinalizationContext = shouldCaptureSettledTurnFinalizationContext ? await captureCodexSettledTurnFinalizationContext({
|
|
5054
|
+
...activeTranscriptTarget,
|
|
5055
|
+
mirroredMessages: mirrorOutcome.mirroredMessages,
|
|
5056
|
+
settledMessages: result.messagesSnapshot,
|
|
5057
|
+
turnId: activeTurnId
|
|
5058
|
+
}) : void 0;
|
|
5059
|
+
if (shouldCaptureSettledTurnFinalizationContext && !settledTurnFinalizationContext) embeddedAgentLog.warn("codex settled-turn finalization context is unavailable", {
|
|
5060
|
+
threadId: resourceState.thread.threadId,
|
|
5061
|
+
turnId: activeTurnId
|
|
5062
|
+
});
|
|
2508
5063
|
if (activeContextEngine) {
|
|
2509
5064
|
const contextEnginePluginId = resolveContextEngineOwnerPluginId(activeContextEngine);
|
|
2510
5065
|
const isHeartbeat = params.bootstrapContextRunKind === "heartbeat" || params.bootstrapContextRunKind === "commitment-only";
|
|
@@ -2635,6 +5190,8 @@ async function finalizeCodexAttempt(resources, turnRuntime, lifecycle, notificat
|
|
|
2635
5190
|
...codexAppServerFailure ? { codexAppServerFailure } : {},
|
|
2636
5191
|
...promptTimeoutOutcome ? { promptTimeoutOutcome } : {},
|
|
2637
5192
|
...assistantTranscriptOwned ? { assistantTranscriptOwned: true } : {},
|
|
5193
|
+
...assistantTranscriptIdempotencyKey ? { assistantTranscriptIdempotencyKey } : {},
|
|
5194
|
+
...settledTurnFinalizationContext ? { settledTurnFinalizationContext } : {},
|
|
2638
5195
|
...resourceState.runtimeArtifact ? { runtimeArtifact: resourceState.runtimeArtifact } : {},
|
|
2639
5196
|
...!finalAborted && !effectiveTimedOut && !finalPromptError && preparedAuthBinding ? { authBindingFingerprint: preparedAuthBinding.fingerprint } : {},
|
|
2640
5197
|
systemPromptReport
|
|
@@ -2851,7 +5408,7 @@ function createCodexAttemptLifecycleController(resources, turnRuntime) {
|
|
|
2851
5408
|
startedAt: attemptStartedAt,
|
|
2852
5409
|
endedAt: Date.now(),
|
|
2853
5410
|
...data,
|
|
2854
|
-
...params.deferTerminalLifecycle
|
|
5411
|
+
...params.deferTerminalLifecycle ? { phase: "finishing" } : {}
|
|
2855
5412
|
}
|
|
2856
5413
|
});
|
|
2857
5414
|
state.lifecycleTerminalEmitted = true;
|
|
@@ -2959,7 +5516,7 @@ var ClientTurnRouter = class {
|
|
|
2959
5516
|
this.nativeTurnCompletionWatchers = /* @__PURE__ */ new Map();
|
|
2960
5517
|
this.disposed = false;
|
|
2961
5518
|
client.addNotificationHandler((notification) => this.routeNotification(notification));
|
|
2962
|
-
client.addRequestHandler((request) => this.routeRequest(request));
|
|
5519
|
+
client.addRequestHandler((request, signal) => this.routeRequest(request, signal));
|
|
2963
5520
|
client.addCloseHandler(() => this.dispose());
|
|
2964
5521
|
}
|
|
2965
5522
|
reserveThread(options) {
|
|
@@ -2974,7 +5531,8 @@ var ClientTurnRouter = class {
|
|
|
2974
5531
|
gate: "open",
|
|
2975
5532
|
pending: [],
|
|
2976
5533
|
notificationTail: Promise.resolve(),
|
|
2977
|
-
nativeTurnCompleted: false
|
|
5534
|
+
nativeTurnCompleted: false,
|
|
5535
|
+
ignoredTurnNotificationKeys: /* @__PURE__ */ new Set()
|
|
2978
5536
|
};
|
|
2979
5537
|
this.routes.set(threadId, route);
|
|
2980
5538
|
if (options.onNotification || options.onRequest) this.activateNow(route, options);
|
|
@@ -3057,6 +5615,7 @@ var ClientTurnRouter = class {
|
|
|
3057
5615
|
this.assertRoute(route);
|
|
3058
5616
|
if (route.gate !== "open") throw new Error(`codex app-server thread route cannot arm from ${route.gate}`);
|
|
3059
5617
|
route.gate = "armed";
|
|
5618
|
+
route.ignoredTurnNotificationKeys.clear();
|
|
3060
5619
|
route.nativeTurnCompleted = false;
|
|
3061
5620
|
route.binding = deferred();
|
|
3062
5621
|
}
|
|
@@ -3106,7 +5665,10 @@ var ClientTurnRouter = class {
|
|
|
3106
5665
|
}
|
|
3107
5666
|
const handler = route.handlers.onNotification;
|
|
3108
5667
|
if (!handler) return;
|
|
3109
|
-
if (route.gate === "bound" && scope.turnId && scope.turnId !== route.turnId)
|
|
5668
|
+
if (route.gate === "bound" && scope.turnId && scope.turnId !== route.turnId) {
|
|
5669
|
+
this.warnDroppedStaleTurnNotification(route, notification, routeScope);
|
|
5670
|
+
return;
|
|
5671
|
+
}
|
|
3110
5672
|
if (route.gate === "armed") {
|
|
3111
5673
|
this.bufferNotification(route, notification, routeScope, receivedAtMs);
|
|
3112
5674
|
return;
|
|
@@ -3115,45 +5677,66 @@ var ClientTurnRouter = class {
|
|
|
3115
5677
|
this.enqueueNotification(route, handler, notification, routeScope);
|
|
3116
5678
|
return route.notificationTail;
|
|
3117
5679
|
}
|
|
3118
|
-
async routeRequest(request) {
|
|
3119
|
-
if (this.disposed) return;
|
|
5680
|
+
async routeRequest(request, signal = new AbortController().signal) {
|
|
5681
|
+
if (this.disposed || signal.aborted) return;
|
|
3120
5682
|
const scope = readScope(request.params);
|
|
3121
5683
|
if (!scope.threadId) return;
|
|
3122
5684
|
const route = this.routes.get(scope.threadId);
|
|
3123
5685
|
if (!route || route.released) return;
|
|
3124
|
-
if (!route.handlers
|
|
3125
|
-
if (route.released || !route.handlers) return;
|
|
5686
|
+
if (!route.handlers && !await waitForPromiseOrAbort(route.activated.promise, signal)) return;
|
|
5687
|
+
if (signal.aborted || route.released || !route.handlers) return;
|
|
3126
5688
|
const handler = route.handlers.onRequest;
|
|
3127
5689
|
if (!handler) return;
|
|
3128
5690
|
while (route.gate === "armed") {
|
|
3129
|
-
|
|
3130
|
-
if (
|
|
5691
|
+
const binding = route.binding?.promise;
|
|
5692
|
+
if (!binding || !await waitForPromiseOrAbort(binding, signal)) return;
|
|
5693
|
+
if (signal.aborted || route.released) return;
|
|
3131
5694
|
}
|
|
3132
5695
|
if (route.gate === "bound") {
|
|
3133
5696
|
if (scope.turnId && scope.turnId !== route.turnId) return;
|
|
3134
5697
|
if (route.released) return;
|
|
3135
5698
|
}
|
|
3136
|
-
await this.waitForNotifications(route);
|
|
3137
|
-
if (route.released) return;
|
|
5699
|
+
if (!await waitForPromiseOrAbort(this.waitForNotifications(route), signal)) return;
|
|
5700
|
+
if (signal.aborted || route.released) return;
|
|
3138
5701
|
try {
|
|
3139
5702
|
const result = await handler(request, {
|
|
3140
5703
|
threadId: scope.threadId,
|
|
3141
5704
|
...scope.turnId ? { turnId: scope.turnId } : {}
|
|
3142
|
-
});
|
|
3143
|
-
return route.released ? void 0 : result;
|
|
5705
|
+
}, signal);
|
|
5706
|
+
return signal.aborted || route.released ? void 0 : result;
|
|
3144
5707
|
} catch (error) {
|
|
3145
|
-
if (route.released) return;
|
|
5708
|
+
if (signal.aborted || route.released) return;
|
|
3146
5709
|
throw error;
|
|
3147
5710
|
}
|
|
3148
5711
|
}
|
|
3149
5712
|
flushNotifications(route) {
|
|
3150
5713
|
const handler = route.handlers?.onNotification;
|
|
3151
5714
|
if (!handler) return;
|
|
3152
|
-
for (const pending of route.pending.splice(0))
|
|
5715
|
+
for (const pending of route.pending.splice(0)) {
|
|
5716
|
+
if (route.gate === "bound" && pending.scope.turnId && pending.scope.turnId !== route.turnId) {
|
|
5717
|
+
this.warnDroppedStaleTurnNotification(route, pending.notification, pending.scope);
|
|
5718
|
+
continue;
|
|
5719
|
+
}
|
|
3153
5720
|
route.handlers?.onNotificationReceived?.(pending.notification, pending.scope, pending.receivedAtMs);
|
|
3154
5721
|
this.enqueueNotification(route, handler, pending.notification, pending.scope);
|
|
3155
5722
|
}
|
|
3156
5723
|
}
|
|
5724
|
+
warnDroppedStaleTurnNotification(route, notification, scope) {
|
|
5725
|
+
if (notification.method === "turn/completed" || !scope.turnId || !route.turnId) return;
|
|
5726
|
+
const eventKind = redactCodexEventKind(notification.method);
|
|
5727
|
+
const key = JSON.stringify([notification.method, scope.turnId]);
|
|
5728
|
+
if (route.ignoredTurnNotificationKeys.has(key)) return;
|
|
5729
|
+
route.ignoredTurnNotificationKeys.add(key);
|
|
5730
|
+
embeddedAgentLog.warn("codex app-server notification ignored for inactive turn", {
|
|
5731
|
+
eventKind,
|
|
5732
|
+
activeThreadId: route.threadId,
|
|
5733
|
+
activeTurnId: route.turnId,
|
|
5734
|
+
threadId: scope.threadId,
|
|
5735
|
+
turnId: scope.turnId,
|
|
5736
|
+
matchesActiveThread: true,
|
|
5737
|
+
matchesActiveTurn: false
|
|
5738
|
+
});
|
|
5739
|
+
}
|
|
3157
5740
|
bufferNotification(route, notification, scope, receivedAtMs) {
|
|
3158
5741
|
if (route.pending.length < DEFAULT_PREBIND_NOTIFICATION_LIMIT) {
|
|
3159
5742
|
route.pending.push({
|
|
@@ -3242,6 +5825,20 @@ function isCodexTerminalTurnNotification(notification) {
|
|
|
3242
5825
|
if (notification.method === "turn/completed") return true;
|
|
3243
5826
|
return notification.method === "error" && isJsonObject(notification.params) && notification.params.willRetry === false;
|
|
3244
5827
|
}
|
|
5828
|
+
async function waitForPromiseOrAbort(promise, signal) {
|
|
5829
|
+
if (signal.aborted) return false;
|
|
5830
|
+
let removeAbort;
|
|
5831
|
+
try {
|
|
5832
|
+
return await Promise.race([promise.then(() => true), new Promise((resolve) => {
|
|
5833
|
+
const onAbort = () => resolve(false);
|
|
5834
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
5835
|
+
removeAbort = () => signal.removeEventListener("abort", onAbort);
|
|
5836
|
+
if (signal.aborted) onAbort();
|
|
5837
|
+
})]);
|
|
5838
|
+
} finally {
|
|
5839
|
+
removeAbort?.();
|
|
5840
|
+
}
|
|
5841
|
+
}
|
|
3245
5842
|
function deferred() {
|
|
3246
5843
|
let resolve;
|
|
3247
5844
|
return {
|
|
@@ -3415,7 +6012,7 @@ function createCodexAttemptNotificationController(resources, turnRuntime, lifecy
|
|
|
3415
6012
|
async function prepareCodexAttemptPrompt(context) {
|
|
3416
6013
|
const { runtime, attemptTools, historyState, hookContext, workspaceBootstrapContext, baseDeveloperInstructions, openClawPromptContext, skillsCollaborationInstructions, promptState, codexContextProjectionMaxChars } = context;
|
|
3417
6014
|
const { connection, buildActiveRunAttemptParams, effectiveContextTokenBudget, effectiveRuntimeModelId, effectiveRuntimeProviderId } = runtime;
|
|
3418
|
-
const { params, activeContextEngine, usesSupervisionConnection, mutable, isInactiveThreadBootstrapBinding, bindingStore, bindingIdentity, agentDir, appServer, contextSessionKey, effectiveWorkspace } = connection;
|
|
6015
|
+
const { params, activeContextEngine, usesSupervisionConnection, mutable, isInactiveThreadBootstrapBinding, bindingStore, bindingIdentity, agentDir, appServer, contextSessionKey, effectiveWorkspace, sandbox } = connection;
|
|
3419
6016
|
const { toolBridge } = attemptTools;
|
|
3420
6017
|
const applyFreshThreadContinuityProjection = () => {
|
|
3421
6018
|
const projection = projectContextEngineAssemblyForCodex({
|
|
@@ -3438,6 +6035,7 @@ async function prepareCodexAttemptPrompt(context) {
|
|
|
3438
6035
|
tokenBudget: effectiveContextTokenBudget,
|
|
3439
6036
|
availableTools: new Set(flattenCodexDynamicToolFunctions(toolBridge.availableSpecs).map((tool) => tool.name).filter(isNonEmptyString)),
|
|
3440
6037
|
citationsMode: params.config?.memory?.citations,
|
|
6038
|
+
sandboxed: sandbox?.enabled === true,
|
|
3441
6039
|
modelId: effectiveRuntimeModelId,
|
|
3442
6040
|
contextEngineHostSupport: CODEX_APP_SERVER_CONTEXT_ENGINE_HOST,
|
|
3443
6041
|
providerId: effectiveRuntimeProviderId,
|
|
@@ -3499,10 +6097,9 @@ async function prepareCodexAttemptPrompt(context) {
|
|
|
3499
6097
|
const buildPromptFromCurrentInputs = () => resolveAgentHarnessBeforePromptBuildResult({
|
|
3500
6098
|
prompt: prependCurrentInboundContext(promptState.promptText, params.currentInboundContext),
|
|
3501
6099
|
developerInstructions: promptState.developerInstructions,
|
|
3502
|
-
messages:
|
|
6100
|
+
messages: structuredClone(historyState.messages),
|
|
3503
6101
|
ctx: hookContext,
|
|
3504
|
-
bootstrapContextRunKind: params.bootstrapContextRunKind
|
|
3505
|
-
..."beforeAgentStartResult" in params ? { beforeAgentStartResult: params.beforeAgentStartResult } : {}
|
|
6102
|
+
bootstrapContextRunKind: params.bootstrapContextRunKind
|
|
3506
6103
|
});
|
|
3507
6104
|
const resolveShiftedPromptInputRange = (prompt, promptInputRange, turnPromptText) => {
|
|
3508
6105
|
if (!promptInputRange || promptInputRange.start < 0 || promptInputRange.end < promptInputRange.start || promptInputRange.end > prompt.length || !turnPromptText.endsWith(prompt)) return;
|
|
@@ -3553,8 +6150,7 @@ async function prepareCodexAttemptPrompt(context) {
|
|
|
3553
6150
|
const buildRenderedCodexDeveloperInstructions = () => joinPresentSections(turnState.promptBuild.developerInstructions, buildTurnCollaborationMode(params, {
|
|
3554
6151
|
turnScopedDeveloperInstructions: workspaceBootstrapContext.turnScopedDeveloperInstructions,
|
|
3555
6152
|
skillsCollaborationInstructions,
|
|
3556
|
-
memoryCollaborationInstructions: workspaceBootstrapContext.memoryCollaborationInstructions
|
|
3557
|
-
heartbeatCollaborationInstructions: workspaceBootstrapContext.heartbeatCollaborationInstructions
|
|
6153
|
+
memoryCollaborationInstructions: workspaceBootstrapContext.memoryCollaborationInstructions
|
|
3558
6154
|
}).settings.developer_instructions ?? void 0);
|
|
3559
6155
|
const rebuildCodexPromptBuildFromCurrentProjection = async () => {
|
|
3560
6156
|
turnState.promptBuild = await buildPromptFromCurrentInputs();
|
|
@@ -5517,7 +8113,7 @@ async function readFile(execServer, params) {
|
|
|
5517
8113
|
const fsBridge = requireFsBridge(execServer);
|
|
5518
8114
|
const stat = await fsBridge.stat({ filePath });
|
|
5519
8115
|
if (!stat) throw new JsonRpcProtocolError(JSON_RPC_NOT_FOUND, "file not found");
|
|
5520
|
-
|
|
8116
|
+
assertSandboxFileReadWithinLimit(stat);
|
|
5521
8117
|
return { dataBase64: (await fsBridge.readFile({ filePath })).toString("base64") };
|
|
5522
8118
|
}
|
|
5523
8119
|
/** Writes base64 data to an existing sandbox directory after write-policy checks. */
|
|
@@ -5658,6 +8254,15 @@ async function copySandboxPath(execServer, params) {
|
|
|
5658
8254
|
}
|
|
5659
8255
|
return;
|
|
5660
8256
|
}
|
|
8257
|
+
if (sourceStat.type === "file" && fsBridge.copyFile) {
|
|
8258
|
+
await fsBridge.copyFile({
|
|
8259
|
+
sourcePath: params.sourcePath,
|
|
8260
|
+
destinationPath: params.destinationPath,
|
|
8261
|
+
mkdir: true
|
|
8262
|
+
});
|
|
8263
|
+
return;
|
|
8264
|
+
}
|
|
8265
|
+
assertSandboxFileReadWithinLimit(sourceStat);
|
|
5661
8266
|
const data = await fsBridge.readFile({ filePath: params.sourcePath });
|
|
5662
8267
|
await fsBridge.writeFile({
|
|
5663
8268
|
filePath: params.destinationPath,
|
|
@@ -5665,6 +8270,9 @@ async function copySandboxPath(execServer, params) {
|
|
|
5665
8270
|
mkdir: true
|
|
5666
8271
|
});
|
|
5667
8272
|
}
|
|
8273
|
+
function assertSandboxFileReadWithinLimit(stat) {
|
|
8274
|
+
if (stat.type === "file" && stat.size > CODEX_SANDBOX_EXEC_SERVER_MAX_READ_FILE_BYTES) throw new Error(`file is too large to read through Codex sandbox exec-server: ${stat.size} bytes`);
|
|
8275
|
+
}
|
|
5668
8276
|
function metadataResponse(stat) {
|
|
5669
8277
|
return {
|
|
5670
8278
|
isDirectory: stat?.type === "directory",
|
|
@@ -6662,16 +9270,17 @@ function prepareCodexAttemptResources(prompt) {
|
|
|
6662
9270
|
const retireSharedCodexClientForOneShotCleanup = async () => {
|
|
6663
9271
|
if (params.cleanupBundleMcpOnRunEnd !== true || state.sharedCodexClientRetiredForOneShotCleanup) return;
|
|
6664
9272
|
state.sharedCodexClientRetiredForOneShotCleanup = true;
|
|
6665
|
-
const retired =
|
|
6666
|
-
embeddedAgentLog.info("codex app-server one-shot cleanup
|
|
9273
|
+
const retired = clearSharedCodexAppServerClientIfCurrentAndUnclaimed(state.client);
|
|
9274
|
+
embeddedAgentLog.info("codex app-server one-shot cleanup checked shared client retirement", {
|
|
6667
9275
|
runId: params.runId,
|
|
6668
9276
|
sessionId: params.sessionId,
|
|
6669
9277
|
sessionKey: params.sessionKey,
|
|
6670
|
-
activeLeases: retired
|
|
6671
|
-
|
|
6672
|
-
|
|
9278
|
+
activeLeases: retired.activeLeases,
|
|
9279
|
+
pendingAcquires: retired.pendingAcquires,
|
|
9280
|
+
closed: retired.closed,
|
|
9281
|
+
matchedSharedClient: retired.found
|
|
6673
9282
|
});
|
|
6674
|
-
if (retired
|
|
9283
|
+
if (retired.closed) await state.client.closeAndWait({
|
|
6675
9284
|
exitTimeoutMs: 2e3,
|
|
6676
9285
|
forceKillDelayMs: 250
|
|
6677
9286
|
});
|
|
@@ -6713,6 +9322,15 @@ function prepareCodexAttemptResources(prompt) {
|
|
|
6713
9322
|
timeoutMs: params.timeoutMs,
|
|
6714
9323
|
timeoutFloorMs: options.startupTimeoutFloorMs
|
|
6715
9324
|
});
|
|
9325
|
+
const requesterChannel = params.messageChannel ?? params.messageProvider;
|
|
9326
|
+
const requester = {
|
|
9327
|
+
...requesterChannel ? { channel: requesterChannel } : {},
|
|
9328
|
+
...params.agentAccountId ? { accountId: params.agentAccountId } : {},
|
|
9329
|
+
...params.senderId ? { senderId: params.senderId } : {},
|
|
9330
|
+
...params.senderIsOwner !== void 0 ? { senderIsOwner: params.senderIsOwner } : {},
|
|
9331
|
+
...params.memberRoleIds?.length ? { roleIds: [...params.memberRoleIds] } : {}
|
|
9332
|
+
};
|
|
9333
|
+
const hasRequester = Object.keys(requester).length > 0;
|
|
6716
9334
|
const buildNativeHookRelayFinalConfigPatch = (decision) => {
|
|
6717
9335
|
state.nativeHookRelay?.unregister();
|
|
6718
9336
|
state.nativeHookRelay = createCodexNativeHookRelay({
|
|
@@ -6726,6 +9344,7 @@ function prepareCodexAttemptResources(prompt) {
|
|
|
6726
9344
|
config: params.config,
|
|
6727
9345
|
runId: params.runId,
|
|
6728
9346
|
channelId: hookChannelId,
|
|
9347
|
+
...hasRequester ? { requester } : {},
|
|
6729
9348
|
attemptTimeoutMs: params.timeoutMs,
|
|
6730
9349
|
startupTimeoutMs,
|
|
6731
9350
|
turnStartTimeoutMs: params.timeoutMs,
|
|
@@ -7071,7 +9690,8 @@ function createCodexAttemptServerRequestController(resources, turnRuntime, lifec
|
|
|
7071
9690
|
const { toolBridge, toolOutcomeOrdinals, suppressedDynamicToolOutcomeOrdinals, allocateCodexToolOutcomeOrdinal } = attemptTools;
|
|
7072
9691
|
const { state, turnIdRef, userInputBridgeRef, openClawDynamicToolExecutions, pendingOpenClawDynamicToolCompletionIds, postToolRawAssistantCompletionIdleTimeoutMs, turnWatches } = turnRuntime;
|
|
7073
9692
|
const { emitExecutionPhaseOnce, scheduleTurnReleaseAfterTerminalDynamicTool, scheduleTerminalDynamicToolReleaseCheck } = lifecycle;
|
|
7074
|
-
const handleServerRequest = async (request, scope) => {
|
|
9693
|
+
const handleServerRequest = async (request, scope, requestSignal = new AbortController().signal) => {
|
|
9694
|
+
const signal = AbortSignal.any([runAbortController.signal, requestSignal]);
|
|
7075
9695
|
const turnId = turnIdRef.current;
|
|
7076
9696
|
const projector = projectorRef.current;
|
|
7077
9697
|
let armCompletionWatchOnResponse = false;
|
|
@@ -7097,7 +9717,7 @@ function createCodexAttemptServerRequestController(resources, turnRuntime, lifec
|
|
|
7097
9717
|
turnId,
|
|
7098
9718
|
pluginAppPolicyContext: resourceState.thread.pluginAppPolicyContext,
|
|
7099
9719
|
...computerUseConfig.enabled ? { computerUseMcpServerName: computerUseConfig.mcpServerName } : {},
|
|
7100
|
-
signal
|
|
9720
|
+
signal
|
|
7101
9721
|
});
|
|
7102
9722
|
}
|
|
7103
9723
|
if (request.method === "item/tool/requestUserInput") {
|
|
@@ -7124,7 +9744,7 @@ function createCodexAttemptServerRequestController(resources, turnRuntime, lifec
|
|
|
7124
9744
|
turnId,
|
|
7125
9745
|
nativeHookRelay: resourceState.nativeHookRelay,
|
|
7126
9746
|
autoApprove: shouldAutoApproveCodexAppServerApprovals(appServer),
|
|
7127
|
-
signal
|
|
9747
|
+
signal,
|
|
7128
9748
|
onNativeToolFailureDisposition: (itemId, disposition) => projector?.recordNativeToolApprovalFailure(itemId, disposition)
|
|
7129
9749
|
});
|
|
7130
9750
|
}
|
|
@@ -7200,7 +9820,7 @@ function createCodexAttemptServerRequestController(resources, turnRuntime, lifec
|
|
|
7200
9820
|
const { execution } = openClawDynamicToolExecutions.claim(call, () => handleDynamicToolCallWithTimeout({
|
|
7201
9821
|
call,
|
|
7202
9822
|
toolBridge,
|
|
7203
|
-
signal
|
|
9823
|
+
signal,
|
|
7204
9824
|
timeoutMs: dynamicToolTimeoutMs,
|
|
7205
9825
|
toolMeta,
|
|
7206
9826
|
toolCallOrdinal,
|
|
@@ -7497,7 +10117,7 @@ function buildMetadataCacheEntryKey(appCacheKey, queryKind) {
|
|
|
7497
10117
|
//#endregion
|
|
7498
10118
|
//#region extensions/codex/src/app-server/plugin-thread-config-deadline.ts
|
|
7499
10119
|
/** Enforces one bounded startup budget across Codex plugin config discovery. */
|
|
7500
|
-
const CODEX_PLUGIN_THREAD_CONFIG_MAX_TIMEOUT_MS =
|
|
10120
|
+
const CODEX_PLUGIN_THREAD_CONFIG_MAX_TIMEOUT_MS = 6e4;
|
|
7501
10121
|
const CODEX_PLUGIN_THREAD_CONFIG_TIMEOUT_DIVISOR = 4;
|
|
7502
10122
|
const CODEX_PLUGIN_THREAD_CONFIG_MIN_TIMEOUT_MS = 100;
|
|
7503
10123
|
var CodexPluginThreadConfigDeadlineError = class extends Error {
|
|
@@ -8075,7 +10695,9 @@ async function startCodexAttemptRuntime(resources) {
|
|
|
8075
10695
|
stream: "codex_app_server.lifecycle",
|
|
8076
10696
|
data: {
|
|
8077
10697
|
phase: "thread_ready",
|
|
8078
|
-
threadId: state.thread.threadId
|
|
10698
|
+
threadId: state.thread.threadId,
|
|
10699
|
+
action: state.thread.lifecycle.action,
|
|
10700
|
+
clientId: state.client.getInstanceId()
|
|
8079
10701
|
}
|
|
8080
10702
|
});
|
|
8081
10703
|
} catch (error) {
|
|
@@ -8299,6 +10921,7 @@ async function prepareCodexAttemptTurnRequest(resources, turnRuntime, ensureCurr
|
|
|
8299
10921
|
model: usesSupervisionConnection ? resourceState.thread.model ?? effectiveRuntimeModelId : params.modelId,
|
|
8300
10922
|
api: usesSupervisionConnection ? runtimeParams.model.api : params.model.api,
|
|
8301
10923
|
transport: appServer.start.transport,
|
|
10924
|
+
observationUnit: "turn",
|
|
8302
10925
|
...hookContextWindowFields,
|
|
8303
10926
|
trace: codexModelCallTrace
|
|
8304
10927
|
},
|
|
@@ -8336,7 +10959,6 @@ async function prepareCodexAttemptTurnRequest(resources, turnRuntime, ensureCurr
|
|
|
8336
10959
|
turnScopedDeveloperInstructions: workspaceBootstrapContext.turnScopedDeveloperInstructions,
|
|
8337
10960
|
skillsCollaborationInstructions: context.skillsCollaborationInstructions,
|
|
8338
10961
|
memoryCollaborationInstructions: workspaceBootstrapContext.memoryCollaborationInstructions,
|
|
8339
|
-
heartbeatCollaborationInstructions: workspaceBootstrapContext.heartbeatCollaborationInstructions,
|
|
8340
10962
|
preserveNativeTurnSettings: usesSupervisionConnection
|
|
8341
10963
|
});
|
|
8342
10964
|
codexModelCallDiagnostics.setRequestPayloadBytes(utf8JsonByteLength(turnStartParams));
|
|
@@ -8561,7 +11183,11 @@ async function startCodexAttemptTurn(resources, turnRuntime, notifications, requ
|
|
|
8561
11183
|
ctx: hookContext,
|
|
8562
11184
|
hookRunner
|
|
8563
11185
|
});
|
|
8564
|
-
|
|
11186
|
+
const bindingReleased = isIncognitoSessionKey(params.sessionKey) ? await bindingStore.mutate(bindingIdentity, {
|
|
11187
|
+
kind: "clear",
|
|
11188
|
+
threadId: resourceState.thread.threadId
|
|
11189
|
+
}) : true;
|
|
11190
|
+
if (!state.timedOut && bindingReleased) await unsubscribeCodexThreadBestEffort(resourceState.client, {
|
|
8565
11191
|
threadId: resourceState.thread.threadId,
|
|
8566
11192
|
timeoutMs: CODEX_APP_SERVER_UNSUBSCRIBE_TIMEOUT_MS
|
|
8567
11193
|
});
|
|
@@ -9002,7 +11628,7 @@ function createCodexAttemptTurnState(resources) {
|
|
|
9002
11628
|
state.resolveCompletion = resolve;
|
|
9003
11629
|
});
|
|
9004
11630
|
const turnCompletionIdleTimeoutMs = resolveCodexTurnCompletionIdleTimeoutMs(options.turnCompletionIdleTimeoutMs ?? appServer.turnCompletionIdleTimeoutMs);
|
|
9005
|
-
const turnAssistantCompletionIdleTimeoutMs = resolveCodexTurnAssistantCompletionIdleTimeoutMs(options.turnAssistantCompletionIdleTimeoutMs);
|
|
11631
|
+
const turnAssistantCompletionIdleTimeoutMs = resolveCodexTurnAssistantCompletionIdleTimeoutMs(options.turnAssistantCompletionIdleTimeoutMs ?? appServer.turnAssistantCompletionIdleTimeoutMs);
|
|
9006
11632
|
const postToolRawAssistantCompletionIdleTimeoutMs = resolveCodexPostToolRawAssistantCompletionIdleTimeoutMs(options.postToolRawAssistantCompletionIdleTimeoutMs ?? appServer.postToolRawAssistantCompletionIdleTimeoutMs, turnAssistantCompletionIdleTimeoutMs);
|
|
9007
11633
|
const turnTerminalIdleTimeoutMs = resolveCodexTurnTerminalIdleTimeoutMs(options.turnTerminalIdleTimeoutMs, params.runTimeoutOverrideMs);
|
|
9008
11634
|
const turnAttemptIdleTimeoutMs = Math.max(100, Math.floor(params.timeoutMs));
|