@openclaw/codex 2026.5.3 → 2026.5.4-beta.2
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/{client-chGfNrq5.js → client-BGbqC7jk.js} +2 -2
- package/dist/{client-factory-MaOMEltT.js → client-factory--cll1Fba.js} +3 -2
- package/dist/command-formatters-PiJcdUbu.js +337 -0
- package/dist/{command-handlers-CeaXhO5y.js → command-handlers-Bbr7RAUs.js} +170 -177
- package/dist/{compact-C0_rGlHZ.js → compact-DcR5aTxd.js} +4 -4
- package/dist/{config-CkkoMeqF.js → config-ByrA30No.js} +0 -2
- package/dist/{conversation-binding-DjptpT9W.js → conversation-binding-IhbXJcXQ.js} +86 -48
- package/dist/doctor-contract-api.js +19 -0
- package/dist/harness.js +3 -3
- package/dist/index.js +8 -3
- package/dist/media-understanding-provider.js +4 -4
- package/dist/{models-Ds7g6191.js → models-CkowdYbm.js} +6 -4
- package/dist/{protocol-validators-Cpopom3_.js → protocol-validators-Dky2yV4W.js} +2354 -2258
- package/dist/provider.js +2 -2
- package/dist/{computer-use-amNk2zrB.js → rate-limit-cache-t6ebYmfS.js} +29 -5
- package/dist/{run-attempt-BNbVe-IG.js → run-attempt-CFL1BFBl.js} +360 -129
- package/dist/{shared-client-0El8eRIE.js → shared-client-Dfk3Enm-.js} +43 -23
- package/dist/test-api.js +2 -2
- package/dist/{thread-lifecycle-OAlp5Pku.js → thread-lifecycle-CzllX4PU.js} +61 -23
- package/package.json +6 -6
- package/dist/capabilities-BmhO5h8O.js +0 -22
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import { i as resolveCodexAppServerRuntimeOptions, r as readCodexPluginConfig } from "./config-
|
|
2
|
-
import { i as readCodexDynamicToolCallParams, r as assertCodexTurnStartResponse, s as readCodexTurn } from "./protocol-validators-
|
|
1
|
+
import { i as resolveCodexAppServerRuntimeOptions, r as readCodexPluginConfig } from "./config-ByrA30No.js";
|
|
2
|
+
import { i as readCodexDynamicToolCallParams, r as assertCodexTurnStartResponse, s as readCodexTurn } from "./protocol-validators-Dky2yV4W.js";
|
|
3
3
|
import { t as isJsonObject } from "./protocol-C9UWI98H.js";
|
|
4
|
-
import { i as isCodexAppServerConnectionClosedError, r as isCodexAppServerApprovalRequest } from "./client-
|
|
5
|
-
import {
|
|
4
|
+
import { i as isCodexAppServerConnectionClosedError, r as isCodexAppServerApprovalRequest } from "./client-BGbqC7jk.js";
|
|
5
|
+
import { r as formatCodexDisplayText, u as formatCodexUsageLimitErrorMessage } from "./command-formatters-PiJcdUbu.js";
|
|
6
|
+
import { a as refreshCodexAppServerAuthTokens, o as resolveCodexAppServerAuthProfileId, s as resolveCodexAppServerAuthProfileIdForAgent, t as clearSharedCodexAppServerClientIfCurrent } from "./shared-client-Dfk3Enm-.js";
|
|
6
7
|
import { i as readCodexAppServerBinding } from "./session-binding-DuJYTJQy.js";
|
|
7
|
-
import { a as
|
|
8
|
-
import { n as defaultCodexAppServerClientFactory, t as createCodexAppServerClientFactoryTestHooks } from "./client-factory
|
|
9
|
-
import {
|
|
8
|
+
import { a as buildTurnStartParams, c as createCodexDynamicToolBridge, l as applyCodexDynamicToolProfile, n as buildDeveloperInstructions, o as codexDynamicToolsFingerprint, s as startOrResumeThread, t as areCodexDynamicToolFingerprintsCompatible } from "./thread-lifecycle-CzllX4PU.js";
|
|
9
|
+
import { n as defaultCodexAppServerClientFactory, t as createCodexAppServerClientFactoryTestHooks } from "./client-factory--cll1Fba.js";
|
|
10
|
+
import { n as rememberCodexRateLimits, r as ensureCodexComputerUse, t as readRecentCodexRateLimits } from "./rate-limit-cache-t6ebYmfS.js";
|
|
10
11
|
import { createHash } from "node:crypto";
|
|
11
12
|
import { TOOL_PROGRESS_OUTPUT_MAX_CHARS, acquireSessionWriteLock, appendSessionTranscriptMessage, assembleHarnessContextEngine, bootstrapHarnessContextEngine, buildEmbeddedAttemptToolRunContext, buildHarnessContextEngineRuntimeContext, buildHarnessContextEngineRuntimeContextFromUsage, callGatewayTool, classifyAgentHarnessTerminalOutcome, clearActiveEmbeddedRun, embeddedAgentLog, emitAgentEvent, emitSessionTranscriptUpdate, finalizeHarnessContextEngineTurn, formatApprovalDisplayPath, formatErrorMessage, formatToolAggregate, formatToolProgressOutput, inferToolMetaFromArgs, isActiveHarnessContextEngine, isSubagentSessionKey, normalizeAgentRuntimeTools, normalizeUsage, registerNativeHookRelay, resolveAgentHarnessBeforePromptBuildResult, resolveAttemptSpawnWorkspaceDir, resolveBootstrapContextForRun, resolveModelAuthMode, resolveOpenClawAgentDir, resolveSandboxContext, resolveSessionAgentIds, resolveSessionWriteLockAcquireTimeoutMs, resolveUserPath, runAgentCleanupStep, runAgentHarnessAfterCompactionHook, runAgentHarnessAgentEndHook, runAgentHarnessBeforeCompactionHook, runAgentHarnessBeforeMessageWriteHook, runAgentHarnessLlmInputHook, runAgentHarnessLlmOutputHook, runHarnessContextEngineMaintenance, setActiveEmbeddedRun, supportsModelTools } from "openclaw/plugin-sdk/agent-harness-runtime";
|
|
12
13
|
import fs from "node:fs/promises";
|
|
@@ -80,6 +81,7 @@ function truncateForGateway(value, maxLength) {
|
|
|
80
81
|
const PERMISSION_DESCRIPTION_MAX_LENGTH = 700;
|
|
81
82
|
const PERMISSION_SAMPLE_LIMIT = 2;
|
|
82
83
|
const PERMISSION_VALUE_MAX_LENGTH = 48;
|
|
84
|
+
const COMMAND_PREVIEW_WITH_DETAILS_MAX_LENGTH = 80;
|
|
83
85
|
const APPROVAL_PREVIEW_SCAN_MAX_LENGTH = 4096;
|
|
84
86
|
const APPROVAL_PREVIEW_OMITTED = "[preview truncated or unsafe content omitted]";
|
|
85
87
|
const ANSI_OSC_SEQUENCE_RE$1 = new RegExp(String.raw`(?:\u001b]|\u009d)[^\u001b\u009c\u0007]*(?:\u0007|\u001b\\|\u009c)`, "g");
|
|
@@ -153,7 +155,7 @@ async function handleCodexAppServerApprovalRequest(params) {
|
|
|
153
155
|
title: context.title,
|
|
154
156
|
...context.eventDetails,
|
|
155
157
|
...approvalEventScope(params.method, cancelled ? "cancelled" : "denied"),
|
|
156
|
-
message: cancelled ? "Codex app-server approval cancelled because the run stopped." : `Codex app-server approval route failed: ${formatErrorMessage$1(error)}`
|
|
158
|
+
message: cancelled ? "Codex app-server approval cancelled because the run stopped." : `Codex app-server approval route failed: ${formatCodexDisplayText(formatErrorMessage$1(error))}`
|
|
157
159
|
});
|
|
158
160
|
return buildApprovalResponse(params.method, context.requestParams, cancelled ? "cancelled" : "denied");
|
|
159
161
|
}
|
|
@@ -181,7 +183,8 @@ function matchesCurrentTurn$1(requestParams, threadId, turnId) {
|
|
|
181
183
|
}
|
|
182
184
|
function buildApprovalContext(params) {
|
|
183
185
|
const itemId = readString$4(params.requestParams, "itemId") ?? readString$4(params.requestParams, "callId") ?? readString$4(params.requestParams, "approvalId");
|
|
184
|
-
const
|
|
186
|
+
const commandDetailLines = params.method === "item/commandExecution/requestApproval" ? describeCommandApprovalDetails(params.requestParams) : [];
|
|
187
|
+
const commandPreview = sanitizeApprovalPreview(readDisplayCommandPreview(params.requestParams), commandDetailLines.length > 0 ? COMMAND_PREVIEW_WITH_DETAILS_MAX_LENGTH : 180);
|
|
185
188
|
const reasonPreview = sanitizeApprovalPreview(readStringPreview(params.requestParams, "reason"), 180);
|
|
186
189
|
const command = commandPreview.text;
|
|
187
190
|
const reason = reasonPreview.text;
|
|
@@ -192,7 +195,11 @@ function buildApprovalContext(params) {
|
|
|
192
195
|
return {
|
|
193
196
|
kind,
|
|
194
197
|
title,
|
|
195
|
-
description: permissionLines.length > 0 ? joinDescriptionLinesWithinLimit(permissionLines, PERMISSION_DESCRIPTION_MAX_LENGTH) : [
|
|
198
|
+
description: permissionLines.length > 0 ? joinDescriptionLinesWithinLimit(permissionLines, PERMISSION_DESCRIPTION_MAX_LENGTH) : [
|
|
199
|
+
subject,
|
|
200
|
+
...commandDetailLines,
|
|
201
|
+
params.paramsForRun.sessionKey && `Session: ${params.paramsForRun.sessionKey}`
|
|
202
|
+
].filter(Boolean).join("\n"),
|
|
196
203
|
severity: kind === "exec" ? "warning" : "info",
|
|
197
204
|
toolName: kind === "exec" ? "codex_command_approval" : params.method === "item/permissions/requestApproval" ? "codex_permission_approval" : "codex_file_approval",
|
|
198
205
|
itemId,
|
|
@@ -235,46 +242,101 @@ function unsupportedApprovalResponse() {
|
|
|
235
242
|
};
|
|
236
243
|
}
|
|
237
244
|
function describeRequestedPermissions(requestParams) {
|
|
238
|
-
|
|
245
|
+
return describePermissionProfile(requestedPermissions(requestParams), "Permissions");
|
|
246
|
+
}
|
|
247
|
+
function describeCommandApprovalDetails(requestParams) {
|
|
248
|
+
const lines = [];
|
|
249
|
+
const additionalPermissions = isJsonObject(requestParams?.additionalPermissions) ? requestParams.additionalPermissions : void 0;
|
|
250
|
+
if (additionalPermissions) lines.push(...describePermissionProfile(additionalPermissions, "Additional permissions"));
|
|
251
|
+
const execpolicySummary = summarizeStringArray(requestParams?.proposedExecpolicyAmendment, "Proposed exec policy", sanitizePermissionScalar);
|
|
252
|
+
if (execpolicySummary) lines.push(execpolicySummary);
|
|
253
|
+
const networkAmendmentSummary = summarizeNetworkPolicyAmendments(requestParams?.proposedNetworkPolicyAmendments);
|
|
254
|
+
if (networkAmendmentSummary) lines.push(networkAmendmentSummary);
|
|
255
|
+
return lines;
|
|
256
|
+
}
|
|
257
|
+
function describePermissionProfile(permissions, label) {
|
|
239
258
|
const lines = [];
|
|
240
259
|
const kinds = [];
|
|
241
260
|
const risks = /* @__PURE__ */ new Set();
|
|
242
261
|
if (isJsonObject(permissions.network)) kinds.push("network");
|
|
243
262
|
if (isJsonObject(permissions.fileSystem)) kinds.push("fileSystem");
|
|
244
|
-
if (kinds.length > 0) lines.push(
|
|
263
|
+
if (kinds.length > 0) lines.push(`${label}: ${kinds.join(", ")}`);
|
|
245
264
|
let networkSummary;
|
|
246
|
-
if (isJsonObject(permissions.network))
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
265
|
+
if (isJsonObject(permissions.network)) {
|
|
266
|
+
const summaries = [summarizeNetworkEnabledPermission(permissions.network, risks), summarizePermissionRecord(permissions.network, risks, [{
|
|
267
|
+
key: "allowHosts",
|
|
268
|
+
label: "allowHosts",
|
|
269
|
+
sanitize: sanitizePermissionHostValue,
|
|
270
|
+
risksFor: permissionHostRisks
|
|
271
|
+
}])].filter((summary) => Boolean(summary));
|
|
272
|
+
networkSummary = summaries.length > 0 ? summaries.join("; ") : void 0;
|
|
273
|
+
}
|
|
252
274
|
let fileSystemSummary;
|
|
253
|
-
if (isJsonObject(permissions.fileSystem))
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
275
|
+
if (isJsonObject(permissions.fileSystem)) {
|
|
276
|
+
const summaries = [summarizePermissionRecord(permissions.fileSystem, risks, [
|
|
277
|
+
{
|
|
278
|
+
key: "read",
|
|
279
|
+
label: "read",
|
|
280
|
+
sanitize: sanitizePermissionPathValue,
|
|
281
|
+
risksFor: permissionPathRisks
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
key: "write",
|
|
285
|
+
label: "write",
|
|
286
|
+
sanitize: sanitizePermissionPathValue,
|
|
287
|
+
risksFor: permissionPathRisks
|
|
288
|
+
},
|
|
289
|
+
{
|
|
290
|
+
key: "roots",
|
|
291
|
+
label: "roots",
|
|
292
|
+
sanitize: sanitizePermissionPathValue,
|
|
293
|
+
risksFor: permissionPathRisks
|
|
294
|
+
},
|
|
295
|
+
{
|
|
296
|
+
key: "readPaths",
|
|
297
|
+
label: "readPaths",
|
|
298
|
+
sanitize: sanitizePermissionPathValue,
|
|
299
|
+
risksFor: permissionPathRisks
|
|
300
|
+
},
|
|
301
|
+
{
|
|
302
|
+
key: "writePaths",
|
|
303
|
+
label: "writePaths",
|
|
304
|
+
sanitize: sanitizePermissionPathValue,
|
|
305
|
+
risksFor: permissionPathRisks
|
|
306
|
+
}
|
|
307
|
+
]), summarizeFileSystemEntries(permissions.fileSystem, risks)].filter((summary) => Boolean(summary));
|
|
308
|
+
fileSystemSummary = summaries.length > 0 ? summaries.join("; ") : void 0;
|
|
309
|
+
}
|
|
273
310
|
if (risks.size > 0) lines.push(`High-risk targets: ${[...risks].join(", ")}`);
|
|
274
311
|
if (networkSummary) lines.push(`Network ${networkSummary}`);
|
|
275
312
|
if (fileSystemSummary) lines.push(`File system ${fileSystemSummary}`);
|
|
276
313
|
return lines;
|
|
277
314
|
}
|
|
315
|
+
function summarizeNetworkEnabledPermission(permission, risks) {
|
|
316
|
+
const enabled = permission.enabled;
|
|
317
|
+
if (typeof enabled !== "boolean") return;
|
|
318
|
+
if (enabled) risks.add("network access");
|
|
319
|
+
return `enabled: ${enabled}`;
|
|
320
|
+
}
|
|
321
|
+
function summarizeFileSystemEntries(permission, risks) {
|
|
322
|
+
const entries = permission.entries;
|
|
323
|
+
if (!Array.isArray(entries)) return;
|
|
324
|
+
const samples = [];
|
|
325
|
+
let count = 0;
|
|
326
|
+
for (const entry of entries) {
|
|
327
|
+
const item = isJsonObject(entry) ? entry : void 0;
|
|
328
|
+
const path = typeof item?.path === "string" ? item.path.trim() : "";
|
|
329
|
+
const access = typeof item?.access === "string" ? item.access.trim() : "";
|
|
330
|
+
if (!path || !access) continue;
|
|
331
|
+
count += 1;
|
|
332
|
+
if (access !== "none") for (const risk of permissionPathRisks(path)) risks.add(risk);
|
|
333
|
+
if (samples.length < PERMISSION_SAMPLE_LIMIT) samples.push(`${sanitizePermissionScalar(access)} ${sanitizePermissionPathValue(path)}`);
|
|
334
|
+
}
|
|
335
|
+
if (count === 0) return;
|
|
336
|
+
const remaining = count - samples.length;
|
|
337
|
+
const remainderSuffix = remaining > 0 ? ` (+${remaining} more)` : "";
|
|
338
|
+
return `entries: ${samples.join(", ")}${remainderSuffix}`;
|
|
339
|
+
}
|
|
278
340
|
function summarizePermissionRecord(permission, risks, descriptors) {
|
|
279
341
|
const details = [];
|
|
280
342
|
for (const descriptor of descriptors) {
|
|
@@ -293,6 +355,32 @@ function summarizePermissionArray(record, descriptor, risks) {
|
|
|
293
355
|
const remainderSuffix = remaining > 0 ? ` (+${remaining} more)` : "";
|
|
294
356
|
return `${descriptor.label}: ${sampleValues.join(", ")}${remainderSuffix}`;
|
|
295
357
|
}
|
|
358
|
+
function summarizeStringArray(value, label, sanitize) {
|
|
359
|
+
if (!Array.isArray(value)) return;
|
|
360
|
+
const values = value.filter((entry) => typeof entry === "string").map((entry) => sanitize(entry)).filter(Boolean);
|
|
361
|
+
if (values.length === 0) return;
|
|
362
|
+
const samples = values.slice(0, PERMISSION_SAMPLE_LIMIT);
|
|
363
|
+
const remaining = values.length - samples.length;
|
|
364
|
+
const remainderSuffix = remaining > 0 ? ` (+${remaining} more)` : "";
|
|
365
|
+
return `${label}: ${samples.join(", ")}${remainderSuffix}`;
|
|
366
|
+
}
|
|
367
|
+
function summarizeNetworkPolicyAmendments(value) {
|
|
368
|
+
if (!Array.isArray(value)) return;
|
|
369
|
+
const samples = [];
|
|
370
|
+
let count = 0;
|
|
371
|
+
for (const entry of value) {
|
|
372
|
+
const amendment = isJsonObject(entry) ? entry : void 0;
|
|
373
|
+
const host = typeof amendment?.host === "string" ? amendment.host : "";
|
|
374
|
+
const action = typeof amendment?.action === "string" ? amendment.action : "";
|
|
375
|
+
if (!host || !action) continue;
|
|
376
|
+
count += 1;
|
|
377
|
+
if (samples.length < PERMISSION_SAMPLE_LIMIT) samples.push(`${sanitizePermissionScalar(action)} ${sanitizePermissionHostValue(host)}`);
|
|
378
|
+
}
|
|
379
|
+
if (count === 0) return;
|
|
380
|
+
const remaining = count - samples.length;
|
|
381
|
+
const remainderSuffix = remaining > 0 ? ` (+${remaining} more)` : "";
|
|
382
|
+
return `Proposed network policy: ${samples.join(", ")}${remainderSuffix}`;
|
|
383
|
+
}
|
|
296
384
|
function readStringArray(record, key) {
|
|
297
385
|
const value = record[key];
|
|
298
386
|
return Array.isArray(value) ? value.map((entry) => typeof entry === "string" ? entry.trim() : "").filter(Boolean) : [];
|
|
@@ -433,7 +521,7 @@ function sanitizeApprovalPreview(source, maxLength) {
|
|
|
433
521
|
const sanitized = sanitizeVisibleScalar(source.value.replace(DANGLING_TERMINAL_SEQUENCE_SUFFIX_RE$1, ""));
|
|
434
522
|
if (!sanitized) return { omitted: true };
|
|
435
523
|
return {
|
|
436
|
-
text: truncate(sanitized, maxLength),
|
|
524
|
+
text: formatCodexDisplayText(truncate(sanitized, maxLength)),
|
|
437
525
|
omitted: source.clipped
|
|
438
526
|
};
|
|
439
527
|
}
|
|
@@ -679,7 +767,8 @@ function sanitizeDisplayText(value) {
|
|
|
679
767
|
const scanned = value.slice(0, DISPLAY_TEXT_SCAN_MAX_LENGTH);
|
|
680
768
|
const clipped = value.length > DISPLAY_TEXT_SCAN_MAX_LENGTH;
|
|
681
769
|
const sanitized = scanned.replace(ANSI_OSC_SEQUENCE_RE, "").replace(ANSI_CONTROL_SEQUENCE_RE, "").replace(DANGLING_TERMINAL_SEQUENCE_SUFFIX_RE, "").replace(INVISIBLE_FORMATTING_CONTROL_RE, " ").replace(CONTROL_CHARACTER_RE, " ").replace(/\s+/g, " ").trim();
|
|
682
|
-
|
|
770
|
+
const escaped = sanitized ? formatCodexDisplayText(sanitized) : "";
|
|
771
|
+
return clipped && escaped ? `${escaped}...` : escaped;
|
|
683
772
|
}
|
|
684
773
|
function truncateDisplayText(value, maxLength) {
|
|
685
774
|
return value.length <= maxLength ? value : `${value.slice(0, Math.max(0, maxLength - 3))}...`;
|
|
@@ -872,6 +961,110 @@ async function readCodexMirroredSessionHistoryMessages(sessionFile) {
|
|
|
872
961
|
}
|
|
873
962
|
}
|
|
874
963
|
//#endregion
|
|
964
|
+
//#region extensions/codex/src/app-server/transcript-mirror.ts
|
|
965
|
+
const MIRROR_IDENTITY_META_KEY = "mirrorIdentity";
|
|
966
|
+
/**
|
|
967
|
+
* Tag a message with a stable logical identity for mirror dedupe. Callers
|
|
968
|
+
* should use a value that is invariant for the same logical message across
|
|
969
|
+
* re-emits (e.g. `${turnId}:prompt`, `${turnId}:assistant`) but distinct
|
|
970
|
+
* for genuinely-distinct messages (different turns, different kinds). When
|
|
971
|
+
* present this identity replaces the role/content fingerprint in the
|
|
972
|
+
* idempotency key, so the dedupe survives caller-scope rotation without
|
|
973
|
+
* collapsing distinct same-content turns.
|
|
974
|
+
*/
|
|
975
|
+
function attachCodexMirrorIdentity(message, identity) {
|
|
976
|
+
const record = message;
|
|
977
|
+
const existing = record.__openclaw;
|
|
978
|
+
const baseMeta = existing && typeof existing === "object" && !Array.isArray(existing) ? existing : {};
|
|
979
|
+
return {
|
|
980
|
+
...record,
|
|
981
|
+
__openclaw: {
|
|
982
|
+
...baseMeta,
|
|
983
|
+
[MIRROR_IDENTITY_META_KEY]: identity
|
|
984
|
+
}
|
|
985
|
+
};
|
|
986
|
+
}
|
|
987
|
+
function readMirrorIdentity(message) {
|
|
988
|
+
const meta = message.__openclaw;
|
|
989
|
+
if (!meta || typeof meta !== "object" || Array.isArray(meta)) return;
|
|
990
|
+
const id = meta[MIRROR_IDENTITY_META_KEY];
|
|
991
|
+
return typeof id === "string" && id.length > 0 ? id : void 0;
|
|
992
|
+
}
|
|
993
|
+
function fingerprintMirrorMessageContent(message) {
|
|
994
|
+
const payload = JSON.stringify({
|
|
995
|
+
role: message.role,
|
|
996
|
+
content: message.content
|
|
997
|
+
});
|
|
998
|
+
return createHash("sha256").update(payload).digest("hex").slice(0, 16);
|
|
999
|
+
}
|
|
1000
|
+
function buildMirrorDedupeIdentity(message) {
|
|
1001
|
+
const explicit = readMirrorIdentity(message);
|
|
1002
|
+
if (explicit) return explicit;
|
|
1003
|
+
return `${message.role}:${fingerprintMirrorMessageContent(message)}`;
|
|
1004
|
+
}
|
|
1005
|
+
async function mirrorCodexAppServerTranscript(params) {
|
|
1006
|
+
const messages = params.messages.filter((message) => message.role === "user" || message.role === "assistant");
|
|
1007
|
+
if (messages.length === 0) return;
|
|
1008
|
+
const lock = await acquireSessionWriteLock({
|
|
1009
|
+
sessionFile: params.sessionFile,
|
|
1010
|
+
timeoutMs: resolveSessionWriteLockAcquireTimeoutMs(params.config)
|
|
1011
|
+
});
|
|
1012
|
+
try {
|
|
1013
|
+
const existingIdempotencyKeys = await readTranscriptIdempotencyKeys(params.sessionFile);
|
|
1014
|
+
for (const message of messages) {
|
|
1015
|
+
const dedupeIdentity = buildMirrorDedupeIdentity(message);
|
|
1016
|
+
const idempotencyKey = params.idempotencyScope ? `${params.idempotencyScope}:${dedupeIdentity}` : void 0;
|
|
1017
|
+
if (idempotencyKey && existingIdempotencyKeys.has(idempotencyKey)) continue;
|
|
1018
|
+
const nextMessage = runAgentHarnessBeforeMessageWriteHook({
|
|
1019
|
+
message: {
|
|
1020
|
+
...message,
|
|
1021
|
+
...idempotencyKey ? { idempotencyKey } : {}
|
|
1022
|
+
},
|
|
1023
|
+
agentId: params.agentId,
|
|
1024
|
+
sessionKey: params.sessionKey
|
|
1025
|
+
});
|
|
1026
|
+
if (!nextMessage) continue;
|
|
1027
|
+
const messageToAppend = idempotencyKey ? {
|
|
1028
|
+
...nextMessage,
|
|
1029
|
+
idempotencyKey
|
|
1030
|
+
} : nextMessage;
|
|
1031
|
+
await appendSessionTranscriptMessage({
|
|
1032
|
+
transcriptPath: params.sessionFile,
|
|
1033
|
+
message: messageToAppend,
|
|
1034
|
+
config: params.config
|
|
1035
|
+
});
|
|
1036
|
+
if (idempotencyKey) existingIdempotencyKeys.add(idempotencyKey);
|
|
1037
|
+
}
|
|
1038
|
+
} finally {
|
|
1039
|
+
await lock.release();
|
|
1040
|
+
}
|
|
1041
|
+
if (params.sessionKey) emitSessionTranscriptUpdate({
|
|
1042
|
+
sessionFile: params.sessionFile,
|
|
1043
|
+
sessionKey: params.sessionKey
|
|
1044
|
+
});
|
|
1045
|
+
else emitSessionTranscriptUpdate(params.sessionFile);
|
|
1046
|
+
}
|
|
1047
|
+
async function readTranscriptIdempotencyKeys(sessionFile) {
|
|
1048
|
+
const keys = /* @__PURE__ */ new Set();
|
|
1049
|
+
let raw;
|
|
1050
|
+
try {
|
|
1051
|
+
raw = await fs.readFile(sessionFile, "utf8");
|
|
1052
|
+
} catch (error) {
|
|
1053
|
+
if (error.code !== "ENOENT") throw error;
|
|
1054
|
+
return keys;
|
|
1055
|
+
}
|
|
1056
|
+
for (const line of raw.split(/\r?\n/)) {
|
|
1057
|
+
if (!line.trim()) continue;
|
|
1058
|
+
try {
|
|
1059
|
+
const parsed = JSON.parse(line);
|
|
1060
|
+
if (typeof parsed.message?.idempotencyKey === "string") keys.add(parsed.message.idempotencyKey);
|
|
1061
|
+
} catch {
|
|
1062
|
+
continue;
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
return keys;
|
|
1066
|
+
}
|
|
1067
|
+
//#endregion
|
|
875
1068
|
//#region extensions/codex/src/app-server/event-projector.ts
|
|
876
1069
|
const ZERO_USAGE = {
|
|
877
1070
|
input: 0,
|
|
@@ -930,6 +1123,11 @@ var CodexAppServerEventProjector = class {
|
|
|
930
1123
|
async handleNotification(notification) {
|
|
931
1124
|
const params = isJsonObject(notification.params) ? notification.params : void 0;
|
|
932
1125
|
if (!params) return;
|
|
1126
|
+
if (notification.method === "account/rateLimits/updated") {
|
|
1127
|
+
this.latestRateLimits = params;
|
|
1128
|
+
rememberCodexRateLimits(params);
|
|
1129
|
+
return;
|
|
1130
|
+
}
|
|
933
1131
|
if (isHookNotificationMethod(notification.method)) {
|
|
934
1132
|
if (!this.isHookNotificationForCurrentThread(params)) return;
|
|
935
1133
|
} else if (!this.isNotificationForTurn(params)) return;
|
|
@@ -978,7 +1176,7 @@ var CodexAppServerEventProjector = class {
|
|
|
978
1176
|
break;
|
|
979
1177
|
case "error":
|
|
980
1178
|
if (readBooleanAlias(params, ["willRetry", "will_retry"]) === true) break;
|
|
981
|
-
this.promptError =
|
|
1179
|
+
this.promptError = this.formatCodexErrorMessage(params) ?? "codex app-server error";
|
|
982
1180
|
this.promptErrorSource = "prompt";
|
|
983
1181
|
break;
|
|
984
1182
|
default: break;
|
|
@@ -989,14 +1187,15 @@ var CodexAppServerEventProjector = class {
|
|
|
989
1187
|
const reasoningText = collectTextValues(this.reasoningTextByItem).join("\n\n");
|
|
990
1188
|
const planText = collectTextValues(this.planTextByItem).join("\n\n");
|
|
991
1189
|
const lastAssistant = assistantTexts.length > 0 ? this.createAssistantMessage(assistantTexts.join("\n\n")) : void 0;
|
|
992
|
-
const
|
|
1190
|
+
const turnId = this.turnId;
|
|
1191
|
+
const messagesSnapshot = [attachCodexMirrorIdentity({
|
|
993
1192
|
role: "user",
|
|
994
1193
|
content: this.params.prompt,
|
|
995
1194
|
timestamp: Date.now()
|
|
996
|
-
}];
|
|
997
|
-
if (reasoningText) messagesSnapshot.push(this.createAssistantMirrorMessage("Codex reasoning", reasoningText));
|
|
998
|
-
if (planText) messagesSnapshot.push(this.createAssistantMirrorMessage("Codex plan", planText));
|
|
999
|
-
if (lastAssistant) messagesSnapshot.push(lastAssistant);
|
|
1195
|
+
}, `${turnId}:prompt`)];
|
|
1196
|
+
if (reasoningText) messagesSnapshot.push(attachCodexMirrorIdentity(this.createAssistantMirrorMessage("Codex reasoning", reasoningText), `${turnId}:reasoning`));
|
|
1197
|
+
if (planText) messagesSnapshot.push(attachCodexMirrorIdentity(this.createAssistantMirrorMessage("Codex plan", planText), `${turnId}:plan`));
|
|
1198
|
+
if (lastAssistant) messagesSnapshot.push(attachCodexMirrorIdentity(lastAssistant, `${turnId}:assistant`));
|
|
1000
1199
|
const turnFailed = this.completedTurn?.status === "failed";
|
|
1001
1200
|
const turnInterrupted = this.completedTurn?.status === "interrupted";
|
|
1002
1201
|
const promptError = this.promptError ?? (turnFailed ? this.completedTurn?.error?.message ?? "codex app-server turn failed" : null);
|
|
@@ -1267,7 +1466,11 @@ var CodexAppServerEventProjector = class {
|
|
|
1267
1466
|
this.completedTurn = turn;
|
|
1268
1467
|
if (turn.status === "interrupted") this.aborted = true;
|
|
1269
1468
|
if (turn.status === "failed") {
|
|
1270
|
-
this.promptError =
|
|
1469
|
+
this.promptError = formatCodexUsageLimitErrorMessage({
|
|
1470
|
+
message: turn.error?.message,
|
|
1471
|
+
codexErrorInfo: turn.error?.codexErrorInfo,
|
|
1472
|
+
rateLimits: this.latestRateLimits ?? readRecentCodexRateLimits()
|
|
1473
|
+
}) ?? turn.error?.message ?? "codex app-server turn failed";
|
|
1271
1474
|
this.promptErrorSource = "prompt";
|
|
1272
1475
|
}
|
|
1273
1476
|
for (const item of turn.items ?? []) {
|
|
@@ -1354,6 +1557,7 @@ var CodexAppServerEventProjector = class {
|
|
|
1354
1557
|
if (!item) return;
|
|
1355
1558
|
const kind = itemKind(item);
|
|
1356
1559
|
if (!kind) return;
|
|
1560
|
+
const meta = itemMeta(item, this.toolProgressDetailMode());
|
|
1357
1561
|
this.emitAgentEvent({
|
|
1358
1562
|
stream: "item",
|
|
1359
1563
|
data: {
|
|
@@ -1363,7 +1567,7 @@ var CodexAppServerEventProjector = class {
|
|
|
1363
1567
|
title: itemTitle(item),
|
|
1364
1568
|
status: params.phase === "start" ? "running" : itemStatus(item),
|
|
1365
1569
|
...itemName(item) ? { name: itemName(item) } : {},
|
|
1366
|
-
...
|
|
1570
|
+
...meta ? { meta } : {}
|
|
1367
1571
|
}
|
|
1368
1572
|
});
|
|
1369
1573
|
}
|
|
@@ -1374,7 +1578,7 @@ var CodexAppServerEventProjector = class {
|
|
|
1374
1578
|
const toolName = itemName(item);
|
|
1375
1579
|
if (!toolName) return;
|
|
1376
1580
|
this.toolResultSummaryItemIds.add(itemId);
|
|
1377
|
-
const meta = itemMeta(item);
|
|
1581
|
+
const meta = itemMeta(item, this.toolProgressDetailMode());
|
|
1378
1582
|
this.emitToolResultMessage({
|
|
1379
1583
|
itemId,
|
|
1380
1584
|
text: formatToolSummary(toolName, meta)
|
|
@@ -1390,7 +1594,7 @@ var CodexAppServerEventProjector = class {
|
|
|
1390
1594
|
if (!toolName || !output) return;
|
|
1391
1595
|
this.emitToolResultMessage({
|
|
1392
1596
|
itemId,
|
|
1393
|
-
text: formatToolOutput(toolName, itemMeta(item), output),
|
|
1597
|
+
text: formatToolOutput(toolName, itemMeta(item, this.toolProgressDetailMode()), output),
|
|
1394
1598
|
finalOutput: true
|
|
1395
1599
|
});
|
|
1396
1600
|
}
|
|
@@ -1406,15 +1610,27 @@ var CodexAppServerEventProjector = class {
|
|
|
1406
1610
|
shouldEmitToolOutput() {
|
|
1407
1611
|
return typeof this.params.shouldEmitToolOutput === "function" ? this.params.shouldEmitToolOutput() : this.params.verboseLevel === "full";
|
|
1408
1612
|
}
|
|
1613
|
+
toolProgressDetailMode() {
|
|
1614
|
+
return this.params.toolProgressDetail === "raw" ? "raw" : "explain";
|
|
1615
|
+
}
|
|
1409
1616
|
recordToolMeta(item) {
|
|
1410
1617
|
if (!item) return;
|
|
1411
1618
|
const toolName = itemName(item);
|
|
1412
1619
|
if (!toolName) return;
|
|
1620
|
+
const meta = itemMeta(item, this.toolProgressDetailMode());
|
|
1413
1621
|
this.toolMetas.set(item.id, {
|
|
1414
1622
|
toolName,
|
|
1415
|
-
...
|
|
1623
|
+
...meta ? { meta } : {}
|
|
1416
1624
|
});
|
|
1417
1625
|
}
|
|
1626
|
+
formatCodexErrorMessage(params) {
|
|
1627
|
+
const error = isJsonObject(params.error) ? params.error : void 0;
|
|
1628
|
+
return formatCodexUsageLimitErrorMessage({
|
|
1629
|
+
message: error ? readString$2(error, "message") : void 0,
|
|
1630
|
+
codexErrorInfo: error?.codexErrorInfo,
|
|
1631
|
+
rateLimits: this.latestRateLimits ?? readRecentCodexRateLimits()
|
|
1632
|
+
}) ?? readCodexErrorNotificationMessage(params);
|
|
1633
|
+
}
|
|
1418
1634
|
emitAgentEvent(event) {
|
|
1419
1635
|
try {
|
|
1420
1636
|
emitAgentEvent({
|
|
@@ -1653,14 +1869,14 @@ function itemName(item) {
|
|
|
1653
1869
|
if (item.type === "fileChange") return "apply_patch";
|
|
1654
1870
|
if (item.type === "webSearch") return "web_search";
|
|
1655
1871
|
}
|
|
1656
|
-
function itemMeta(item) {
|
|
1872
|
+
function itemMeta(item, detailMode = "explain") {
|
|
1657
1873
|
if (item.type === "commandExecution" && typeof item.command === "string") return inferToolMetaFromArgs("exec", {
|
|
1658
1874
|
command: item.command,
|
|
1659
1875
|
cwd: typeof item.cwd === "string" ? item.cwd : void 0
|
|
1660
|
-
});
|
|
1876
|
+
}, { detailMode });
|
|
1661
1877
|
if (item.type === "webSearch" && typeof item.query === "string") return item.query;
|
|
1662
1878
|
const toolName = itemName(item);
|
|
1663
|
-
if ((item.type === "dynamicToolCall" || item.type === "mcpToolCall") && toolName) return inferToolMetaFromArgs(toolName, item.arguments);
|
|
1879
|
+
if ((item.type === "dynamicToolCall" || item.type === "mcpToolCall") && toolName) return inferToolMetaFromArgs(toolName, item.arguments, { detailMode });
|
|
1664
1880
|
}
|
|
1665
1881
|
function itemOutputText(item) {
|
|
1666
1882
|
if (item.type === "commandExecution") return item.aggregatedOutput?.trim() || void 0;
|
|
@@ -2014,69 +2230,6 @@ function normalizeCodexTrajectoryError(value) {
|
|
|
2014
2230
|
}
|
|
2015
2231
|
}
|
|
2016
2232
|
//#endregion
|
|
2017
|
-
//#region extensions/codex/src/app-server/transcript-mirror.ts
|
|
2018
|
-
async function mirrorCodexAppServerTranscript(params) {
|
|
2019
|
-
const messages = params.messages.filter((message) => message.role === "user" || message.role === "assistant");
|
|
2020
|
-
if (messages.length === 0) return;
|
|
2021
|
-
const lock = await acquireSessionWriteLock({
|
|
2022
|
-
sessionFile: params.sessionFile,
|
|
2023
|
-
timeoutMs: resolveSessionWriteLockAcquireTimeoutMs(params.config)
|
|
2024
|
-
});
|
|
2025
|
-
try {
|
|
2026
|
-
const existingIdempotencyKeys = await readTranscriptIdempotencyKeys(params.sessionFile);
|
|
2027
|
-
for (const [index, message] of messages.entries()) {
|
|
2028
|
-
const idempotencyKey = params.idempotencyScope ? `${params.idempotencyScope}:${message.role}:${index}` : void 0;
|
|
2029
|
-
if (idempotencyKey && existingIdempotencyKeys.has(idempotencyKey)) continue;
|
|
2030
|
-
const nextMessage = runAgentHarnessBeforeMessageWriteHook({
|
|
2031
|
-
message: {
|
|
2032
|
-
...message,
|
|
2033
|
-
...idempotencyKey ? { idempotencyKey } : {}
|
|
2034
|
-
},
|
|
2035
|
-
agentId: params.agentId,
|
|
2036
|
-
sessionKey: params.sessionKey
|
|
2037
|
-
});
|
|
2038
|
-
if (!nextMessage) continue;
|
|
2039
|
-
const messageToAppend = idempotencyKey ? {
|
|
2040
|
-
...nextMessage,
|
|
2041
|
-
idempotencyKey
|
|
2042
|
-
} : nextMessage;
|
|
2043
|
-
await appendSessionTranscriptMessage({
|
|
2044
|
-
transcriptPath: params.sessionFile,
|
|
2045
|
-
message: messageToAppend,
|
|
2046
|
-
config: params.config
|
|
2047
|
-
});
|
|
2048
|
-
if (idempotencyKey) existingIdempotencyKeys.add(idempotencyKey);
|
|
2049
|
-
}
|
|
2050
|
-
} finally {
|
|
2051
|
-
await lock.release();
|
|
2052
|
-
}
|
|
2053
|
-
if (params.sessionKey) emitSessionTranscriptUpdate({
|
|
2054
|
-
sessionFile: params.sessionFile,
|
|
2055
|
-
sessionKey: params.sessionKey
|
|
2056
|
-
});
|
|
2057
|
-
else emitSessionTranscriptUpdate(params.sessionFile);
|
|
2058
|
-
}
|
|
2059
|
-
async function readTranscriptIdempotencyKeys(sessionFile) {
|
|
2060
|
-
const keys = /* @__PURE__ */ new Set();
|
|
2061
|
-
let raw;
|
|
2062
|
-
try {
|
|
2063
|
-
raw = await fs.readFile(sessionFile, "utf8");
|
|
2064
|
-
} catch (error) {
|
|
2065
|
-
if (error.code !== "ENOENT") throw error;
|
|
2066
|
-
return keys;
|
|
2067
|
-
}
|
|
2068
|
-
for (const line of raw.split(/\r?\n/)) {
|
|
2069
|
-
if (!line.trim()) continue;
|
|
2070
|
-
try {
|
|
2071
|
-
const parsed = JSON.parse(line);
|
|
2072
|
-
if (typeof parsed.message?.idempotencyKey === "string") keys.add(parsed.message.idempotencyKey);
|
|
2073
|
-
} catch {
|
|
2074
|
-
continue;
|
|
2075
|
-
}
|
|
2076
|
-
}
|
|
2077
|
-
return keys;
|
|
2078
|
-
}
|
|
2079
|
-
//#endregion
|
|
2080
2233
|
//#region extensions/codex/src/app-server/user-input-bridge.ts
|
|
2081
2234
|
function createCodexUserInputBridge(params) {
|
|
2082
2235
|
let pending;
|
|
@@ -2092,6 +2245,7 @@ function createCodexUserInputBridge(params) {
|
|
|
2092
2245
|
const requestParams = readUserInputParams(request.params);
|
|
2093
2246
|
if (!requestParams) return;
|
|
2094
2247
|
if (requestParams.threadId !== params.threadId || requestParams.turnId !== params.turnId) return;
|
|
2248
|
+
if (requestParams.questions.length === 0) return emptyUserInputResponse();
|
|
2095
2249
|
resolvePending(emptyUserInputResponse());
|
|
2096
2250
|
return new Promise((resolve) => {
|
|
2097
2251
|
const abortListener = () => resolvePending(emptyUserInputResponse());
|
|
@@ -2186,11 +2340,11 @@ async function deliverUserInputPrompt(params, questions) {
|
|
|
2186
2340
|
function formatUserInputPrompt(questions) {
|
|
2187
2341
|
const lines = ["Codex needs input:"];
|
|
2188
2342
|
questions.forEach((question, index) => {
|
|
2189
|
-
if (questions.length > 1) lines.push("", `${index + 1}. ${question.header}`, question.question);
|
|
2190
|
-
else lines.push("", question.header, question.question);
|
|
2343
|
+
if (questions.length > 1) lines.push("", `${index + 1}. ${formatCodexDisplayText(question.header)}`, formatCodexDisplayText(question.question));
|
|
2344
|
+
else lines.push("", formatCodexDisplayText(question.header), formatCodexDisplayText(question.question));
|
|
2191
2345
|
if (question.isSecret) lines.push("This channel may show your reply to other participants.");
|
|
2192
2346
|
question.options?.forEach((option, optionIndex) => {
|
|
2193
|
-
lines.push(`${optionIndex + 1}. ${option.label}${option.description ? ` - ${option.description}` : ""}`);
|
|
2347
|
+
lines.push(`${optionIndex + 1}. ${formatCodexDisplayText(option.label)}${option.description ? ` - ${formatCodexDisplayText(option.description)}` : ""}`);
|
|
2194
2348
|
});
|
|
2195
2349
|
if (question.isOther) lines.push("Other: reply with your own answer.");
|
|
2196
2350
|
});
|
|
@@ -2200,14 +2354,18 @@ function buildUserInputResponse(questions, inputText) {
|
|
|
2200
2354
|
const answers = {};
|
|
2201
2355
|
if (questions.length === 1) {
|
|
2202
2356
|
const question = questions[0];
|
|
2203
|
-
if (question)
|
|
2357
|
+
if (question) {
|
|
2358
|
+
const answer = normalizeAnswer(inputText, question);
|
|
2359
|
+
answers[question.id] = { answers: answer ? [answer] : [] };
|
|
2360
|
+
}
|
|
2204
2361
|
return { answers };
|
|
2205
2362
|
}
|
|
2206
2363
|
const keyed = parseKeyedAnswers(inputText);
|
|
2207
2364
|
const fallbackLines = inputText.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
|
|
2208
2365
|
questions.forEach((question, index) => {
|
|
2209
2366
|
const answer = keyed.get(question.id.toLowerCase()) ?? keyed.get(question.header.toLowerCase()) ?? keyed.get(question.question.toLowerCase()) ?? keyed.get(String(index + 1)) ?? fallbackLines[index] ?? "";
|
|
2210
|
-
|
|
2367
|
+
const normalized = answer ? normalizeAnswer(answer, question) : void 0;
|
|
2368
|
+
answers[question.id] = { answers: normalized ? [normalized] : [] };
|
|
2211
2369
|
});
|
|
2212
2370
|
return { answers };
|
|
2213
2371
|
}
|
|
@@ -2217,7 +2375,10 @@ function normalizeAnswer(answer, question) {
|
|
|
2217
2375
|
const optionIndex = /^\d+$/.test(trimmed) ? Number(trimmed) - 1 : -1;
|
|
2218
2376
|
const indexed = optionIndex >= 0 ? options[optionIndex] : void 0;
|
|
2219
2377
|
if (indexed) return indexed.label;
|
|
2220
|
-
|
|
2378
|
+
const exact = options.find((option) => option.label.toLowerCase() === trimmed.toLowerCase());
|
|
2379
|
+
if (exact) return exact.label;
|
|
2380
|
+
if (options.length > 0 && !question.isOther) return;
|
|
2381
|
+
return trimmed || void 0;
|
|
2221
2382
|
}
|
|
2222
2383
|
function parseKeyedAnswers(inputText) {
|
|
2223
2384
|
const answers = /* @__PURE__ */ new Map();
|
|
@@ -2408,7 +2569,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
2408
2569
|
const appServer = resolveCodexAppServerRuntimeOptions({ pluginConfig });
|
|
2409
2570
|
const resolvedWorkspace = resolveUserPath(params.workspaceDir);
|
|
2410
2571
|
await fs.mkdir(resolvedWorkspace, { recursive: true });
|
|
2411
|
-
const sandboxSessionKey = params.sessionKey?.trim() || params.sessionId;
|
|
2572
|
+
const sandboxSessionKey = params.sandboxSessionKey?.trim() || params.sessionKey?.trim() || params.sessionId;
|
|
2412
2573
|
const sandbox = await resolveSandboxContext({
|
|
2413
2574
|
config: params.config,
|
|
2414
2575
|
sessionKey: sandboxSessionKey,
|
|
@@ -2463,6 +2624,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
2463
2624
|
signal: runAbortController.signal,
|
|
2464
2625
|
hookContext: {
|
|
2465
2626
|
agentId: sessionAgentId,
|
|
2627
|
+
config: params.config,
|
|
2466
2628
|
sessionId: params.sessionId,
|
|
2467
2629
|
sessionKey: sandboxSessionKey,
|
|
2468
2630
|
runId: params.runId
|
|
@@ -2528,6 +2690,19 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
2528
2690
|
} catch (assembleErr) {
|
|
2529
2691
|
embeddedAgentLog.warn("context engine assemble failed; using Codex baseline prompt", { error: formatErrorMessage(assembleErr) });
|
|
2530
2692
|
}
|
|
2693
|
+
else if (shouldProjectMirroredHistoryForCodexStart({
|
|
2694
|
+
startupBinding,
|
|
2695
|
+
dynamicToolsFingerprint: codexDynamicToolsFingerprint(toolBridge.specs),
|
|
2696
|
+
historyMessages
|
|
2697
|
+
})) {
|
|
2698
|
+
const projection = projectContextEngineAssemblyForCodex({
|
|
2699
|
+
assembledMessages: historyMessages,
|
|
2700
|
+
originalHistoryMessages: historyMessages,
|
|
2701
|
+
prompt: params.prompt
|
|
2702
|
+
});
|
|
2703
|
+
promptText = projection.promptText;
|
|
2704
|
+
prePromptMessageCount = projection.prePromptMessageCount;
|
|
2705
|
+
}
|
|
2531
2706
|
const promptBuild = await resolveAgentHarnessBeforePromptBuildResult({
|
|
2532
2707
|
prompt: promptText,
|
|
2533
2708
|
developerInstructions,
|
|
@@ -2563,6 +2738,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
2563
2738
|
agentId: sessionAgentId,
|
|
2564
2739
|
sessionId: params.sessionId,
|
|
2565
2740
|
sessionKey: sandboxSessionKey,
|
|
2741
|
+
config: params.config,
|
|
2566
2742
|
runId: params.runId,
|
|
2567
2743
|
signal: runAbortController.signal
|
|
2568
2744
|
});
|
|
@@ -2578,7 +2754,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
2578
2754
|
operation: async () => {
|
|
2579
2755
|
let attemptedClient;
|
|
2580
2756
|
const startupAttempt = async () => {
|
|
2581
|
-
const startupClient = await attemptClientFactory(appServer.start, startupAuthProfileId, agentDir);
|
|
2757
|
+
const startupClient = await attemptClientFactory(appServer.start, startupAuthProfileId, agentDir, params.config);
|
|
2582
2758
|
attemptedClient = startupClient;
|
|
2583
2759
|
startupClientForCleanup = startupClient;
|
|
2584
2760
|
await ensureCodexComputerUse({
|
|
@@ -2950,11 +3126,13 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
2950
3126
|
signal: runAbortController.signal
|
|
2951
3127
|
}));
|
|
2952
3128
|
} catch (error) {
|
|
3129
|
+
const usageLimitError = formatCodexTurnStartUsageLimitError(error, pendingNotifications);
|
|
3130
|
+
const turnStartErrorMessage = usageLimitError ?? formatErrorMessage(error);
|
|
2953
3131
|
emitCodexAppServerEvent(params, {
|
|
2954
3132
|
stream: "codex_app_server.lifecycle",
|
|
2955
3133
|
data: {
|
|
2956
3134
|
phase: "turn_start_failed",
|
|
2957
|
-
error:
|
|
3135
|
+
error: turnStartErrorMessage
|
|
2958
3136
|
}
|
|
2959
3137
|
});
|
|
2960
3138
|
trajectoryRecorder?.recordEvent("session.ended", {
|
|
@@ -2962,7 +3140,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
2962
3140
|
threadId: thread.threadId,
|
|
2963
3141
|
timedOut,
|
|
2964
3142
|
aborted: runAbortController.signal.aborted,
|
|
2965
|
-
promptError:
|
|
3143
|
+
promptError: turnStartErrorMessage
|
|
2966
3144
|
});
|
|
2967
3145
|
trajectoryEndRecorded = true;
|
|
2968
3146
|
runAgentHarnessLlmOutputHook({
|
|
@@ -2981,7 +3159,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
2981
3159
|
event: {
|
|
2982
3160
|
messages: turnStartFailureMessages,
|
|
2983
3161
|
success: false,
|
|
2984
|
-
error:
|
|
3162
|
+
error: turnStartErrorMessage,
|
|
2985
3163
|
durationMs: Date.now() - attemptStartedAt
|
|
2986
3164
|
},
|
|
2987
3165
|
ctx: hookContext
|
|
@@ -2999,6 +3177,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
2999
3177
|
}
|
|
3000
3178
|
});
|
|
3001
3179
|
params.abortSignal?.removeEventListener("abort", abortFromUpstream);
|
|
3180
|
+
if (usageLimitError) throw new Error(usageLimitError, { cause: error });
|
|
3002
3181
|
throw error;
|
|
3003
3182
|
}
|
|
3004
3183
|
turnId = turn.turn.id;
|
|
@@ -3265,6 +3444,7 @@ function createCodexNativeHookRelay(params) {
|
|
|
3265
3444
|
...params.agentId ? { agentId: params.agentId } : {},
|
|
3266
3445
|
sessionId: params.sessionId,
|
|
3267
3446
|
...params.sessionKey ? { sessionKey: params.sessionKey } : {},
|
|
3447
|
+
...params.config ? { config: params.config } : {},
|
|
3268
3448
|
runId: params.runId,
|
|
3269
3449
|
allowedEvents: params.options?.events ?? CODEX_NATIVE_HOOK_RELAY_EVENTS,
|
|
3270
3450
|
ttlMs: params.options?.ttlMs,
|
|
@@ -3315,6 +3495,7 @@ async function buildDynamicTools(input) {
|
|
|
3315
3495
|
senderIsOwner: params.senderIsOwner,
|
|
3316
3496
|
allowGatewaySubagentBinding: params.allowGatewaySubagentBinding,
|
|
3317
3497
|
sessionKey: input.sandboxSessionKey,
|
|
3498
|
+
runSessionKey: params.sessionKey && params.sessionKey !== input.sandboxSessionKey ? params.sessionKey : void 0,
|
|
3318
3499
|
sessionId: params.sessionId,
|
|
3319
3500
|
runId: params.runId,
|
|
3320
3501
|
agentDir,
|
|
@@ -3371,6 +3552,14 @@ async function buildDynamicTools(input) {
|
|
|
3371
3552
|
model: params.model
|
|
3372
3553
|
});
|
|
3373
3554
|
}
|
|
3555
|
+
function shouldProjectMirroredHistoryForCodexStart(params) {
|
|
3556
|
+
if (!params.historyMessages.some((message) => message.role === "user")) return false;
|
|
3557
|
+
if (!params.startupBinding?.threadId) return true;
|
|
3558
|
+
return !areCodexDynamicToolFingerprintsCompatible({
|
|
3559
|
+
previous: params.startupBinding.dynamicToolsFingerprint,
|
|
3560
|
+
next: params.dynamicToolsFingerprint
|
|
3561
|
+
});
|
|
3562
|
+
}
|
|
3374
3563
|
async function withCodexStartupTimeout(params) {
|
|
3375
3564
|
if (params.signal.aborted) throw new Error("codex app-server startup aborted");
|
|
3376
3565
|
let timeout;
|
|
@@ -3410,6 +3599,48 @@ function resolveCodexTurnTerminalIdleTimeoutMs(value) {
|
|
|
3410
3599
|
function readDynamicToolCallParams(value) {
|
|
3411
3600
|
return readCodexDynamicToolCallParams(value);
|
|
3412
3601
|
}
|
|
3602
|
+
function formatCodexTurnStartUsageLimitError(error, pendingNotifications) {
|
|
3603
|
+
const notificationError = readLatestCodexErrorNotification(pendingNotifications);
|
|
3604
|
+
const errorPayload = readCodexErrorPayload(error);
|
|
3605
|
+
return formatCodexUsageLimitErrorMessage({
|
|
3606
|
+
message: notificationError?.message ?? errorPayload.message ?? formatErrorMessage(error),
|
|
3607
|
+
codexErrorInfo: notificationError?.codexErrorInfo ?? errorPayload.codexErrorInfo,
|
|
3608
|
+
rateLimits: readLatestRateLimitNotificationPayload(pendingNotifications) ?? errorPayload.rateLimits ?? readRecentCodexRateLimits()
|
|
3609
|
+
});
|
|
3610
|
+
}
|
|
3611
|
+
function readLatestRateLimitNotificationPayload(notifications) {
|
|
3612
|
+
for (let index = notifications.length - 1; index >= 0; index -= 1) {
|
|
3613
|
+
const notification = notifications[index];
|
|
3614
|
+
if (notification?.method === "account/rateLimits/updated") {
|
|
3615
|
+
rememberCodexRateLimits(notification.params);
|
|
3616
|
+
return notification.params;
|
|
3617
|
+
}
|
|
3618
|
+
}
|
|
3619
|
+
}
|
|
3620
|
+
function readLatestCodexErrorNotification(notifications) {
|
|
3621
|
+
for (let index = notifications.length - 1; index >= 0; index -= 1) {
|
|
3622
|
+
const notification = notifications[index];
|
|
3623
|
+
if (notification?.method !== "error" || !isJsonObject(notification.params)) continue;
|
|
3624
|
+
const error = notification.params.error;
|
|
3625
|
+
if (!isJsonObject(error)) continue;
|
|
3626
|
+
return {
|
|
3627
|
+
message: readString(error, "message"),
|
|
3628
|
+
codexErrorInfo: error.codexErrorInfo
|
|
3629
|
+
};
|
|
3630
|
+
}
|
|
3631
|
+
}
|
|
3632
|
+
function readCodexErrorPayload(error) {
|
|
3633
|
+
const message = error instanceof Error ? error.message : void 0;
|
|
3634
|
+
if (!error || typeof error !== "object" || !("data" in error)) return { message };
|
|
3635
|
+
const data = error.data;
|
|
3636
|
+
if (!isJsonObject(data)) return { message };
|
|
3637
|
+
const nestedError = isJsonObject(data.error) ? data.error : data;
|
|
3638
|
+
return {
|
|
3639
|
+
message: readString(nestedError, "message") ?? message,
|
|
3640
|
+
codexErrorInfo: nestedError.codexErrorInfo,
|
|
3641
|
+
rateLimits: nestedError.rateLimits ?? data.rateLimits
|
|
3642
|
+
};
|
|
3643
|
+
}
|
|
3413
3644
|
function isTurnNotification(value, threadId, turnId) {
|
|
3414
3645
|
if (!isJsonObject(value)) return false;
|
|
3415
3646
|
return readString(value, "threadId") === threadId && readNotificationTurnId(value) === turnId;
|
|
@@ -3512,7 +3743,7 @@ async function mirrorTranscriptBestEffort(params) {
|
|
|
3512
3743
|
agentId: params.agentId,
|
|
3513
3744
|
sessionKey: params.sessionKey,
|
|
3514
3745
|
messages: params.result.messagesSnapshot,
|
|
3515
|
-
idempotencyScope: `codex-app-server:${params.threadId}
|
|
3746
|
+
idempotencyScope: `codex-app-server:${params.threadId}`,
|
|
3516
3747
|
config: params.params.config
|
|
3517
3748
|
});
|
|
3518
3749
|
} catch (error) {
|