@openclaw/codex 2026.7.2-beta.7 → 2026.8.1-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/api.js +147 -0
- package/dist/app-server-policy-bg5QPSVP.js +25 -0
- package/dist/attempt-client-cleanup-URjUlCHg.js +735 -0
- package/dist/binding-connection-DAvZwp96.js +45 -0
- package/dist/{bounded-turn-D2ZAgZVz.js → bounded-turn-4BjrZEwW.js} +63 -56
- package/dist/{capabilities-BFDTVYr4.js → capabilities-CH3K57v4.js} +8 -2
- package/dist/cli-metadata.js +1 -1
- package/dist/{command-formatters-C336eRVS.js → command-formatters-D0nZ3qtd.js} +18 -18
- package/dist/{command-handlers-DAHUSry9.js → command-handlers-DC86lgT4.js} +507 -101
- package/dist/command-rpc-xUIIonsO.js +90 -0
- package/dist/{compact-D_uvHurS.js → compact-COqgBRny.js} +234 -214
- package/dist/{session-binding-BXpo9ymA.js → config-CpNeo5AE.js} +284 -1163
- package/dist/context-compaction-activity-BOeJf7yo.js +58 -0
- package/dist/{conversation-binding-data-BHRpYbX3.js → conversation-binding-data-CW2GuupV.js} +6 -10
- package/dist/doctor-contract-api.js +48 -50
- package/dist/{dynamic-tools-D2gYkSBO.js → dynamic-tools-fJZhl4CE.js} +3442 -1433
- package/dist/{effective-mcp-catalog-Bbd98Inw.js → effective-mcp-catalog-4YKn51aG.js} +20 -15
- package/dist/elicitation-input-DMkeki8d.js +71 -0
- package/dist/{event-projector-assistant-message-DTgnPPVF.js → event-projector-assistant-message-BwxV4d6N.js} +11 -23
- package/dist/{attempt-notifications-AP-a-Q5k.js → event-projector-usage-CLwNEJqW.js} +113 -29
- package/dist/harness-DoKQcF8n.js +258 -0
- package/dist/harness.js +2 -160
- package/dist/image-payload-sanitizer-CnfoUPCE.js +65 -0
- package/dist/index.js +730 -256
- package/dist/isolated-completion-DPMtlH9r.js +75 -0
- package/dist/local-runtime-attribution-B_2mnqhb.js +19 -0
- package/dist/managed-binary-By4FZC6i.js +212 -0
- package/dist/media-understanding-provider.js +5 -5
- package/dist/model-catalog-CmGoqVKB.js +48 -0
- package/dist/{models-BBMKU4jA.js → models-CZw1wClT.js} +12 -17
- package/dist/native-subagent-monitor-GGq3RnOo.js +3225 -0
- package/dist/node-exec-server.runtime-Bsu1LdFZ.js +192 -0
- package/dist/{plugin-app-cache-key-DztPafOA.js → plugin-app-cache-key-DMNQ8Zi8.js} +18 -6
- package/dist/plugin-approval-roundtrip-C75Yiiwc.js +95 -0
- package/dist/plugin-marketplace-discovery-CSWDkMrP.js +399 -0
- package/dist/{computer-use-CYEc8U3w.js → plugin-metadata-cache-BXH5NF-4.js} +109 -6
- package/dist/protocol-validators-VAVyEnDp.js +2399 -0
- package/dist/{rate-limits-CU-gVCCS.js → rate-limits-BI1dMHDG.js} +17 -35
- package/dist/{request-qKh331yu.js → request-DSK0W6YI.js} +3 -3
- package/dist/{run-attempt-CY9WlDLc.js → run-attempt-BV3HzuCf.js} +3416 -7194
- package/dist/{runtime-artifact-CmtG_t1L.js → runtime-artifact-BvyT1uyd.js} +11 -29
- package/dist/{sandbox-guard-NuhpXmPB.js → sandbox-guard-C1qEeZ5C.js} +29 -30
- package/dist/session-binding-BTrLf9Qm.js +874 -0
- package/dist/{session-catalog-DyOgR4WH.js → session-catalog-ezP_UcRM.js} +1584 -1289
- package/dist/session-catalog-home-id-BNwAJ9D7.js +18 -0
- package/dist/{session-cli-2PZaFLRh.js → session-cli-B1hKvfkI.js} +20 -7
- package/dist/session-retirement-CE4iMiLh.js +36 -0
- package/dist/{session-upstream-marker-B6U7tLw5.js → session-upstream-marker-D_2npHlA.js} +1 -1
- package/dist/{settled-turn-finalizer-CXY2Z9dT.js → settled-turn-finalizer-Dx1onIqg.js} +40 -28
- package/dist/{shared-client-DTbvH-XV.js → shared-client-Dw-rzii0.js} +1315 -469
- package/dist/{side-question-_ulBk1AL.js → side-question-DbXkJZzg.js} +140 -69
- package/dist/{thread-lifecycle-DF53ogiA.js → thread-lifecycle-DKdrnTMX.js} +927 -768
- package/dist/thread-ownership-i2NBwDUL.js +92 -0
- package/dist/thread-resume-CdJk2Ehf.js +30 -0
- package/dist/transcript-mirror-BU0_LKAL.js +588 -0
- package/dist/{transport-stdio-DXgGZ7Ib.js → transport-stdio-DhoGy33Z.js} +13 -2
- package/dist/upstream-prompt-provenance-CFmsKwr9.js +39 -0
- package/dist/{upstream-session-fork-Dy4Xvuoj.js → upstream-session-fork-BDVJCLAD.js} +14 -8
- package/dist/{usage-CQ6GhD5-.js → usage-9dKJCgcy.js} +3 -3
- package/dist/{web-search-provider.runtime-CCFhg2z5.js → web-search-provider.runtime-DCdx4DGu.js} +8 -9
- package/openclaw.plugin.json +49 -4
- package/package.json +6 -5
- package/dist/app-server-policy-BSaO2JIT.js +0 -49
- package/dist/conversation-control-xsIeXbo4.js +0 -287
- package/dist/event-projector-usage-Xp4AlR2k.js +0 -86
- package/dist/notification-correlation-CU6_AnOb.js +0 -67
- package/dist/transcript-mirror-DzsdNCf5.js +0 -6924
|
@@ -0,0 +1,735 @@
|
|
|
1
|
+
import { B as CodexAppServerRpcError, X as unsubscribeCodexAppServerLiveThread, p as retireSharedCodexAppServerClientIfCurrent, ut as isJsonObject } from "./shared-client-Dw-rzii0.js";
|
|
2
|
+
import { asFiniteNumber, normalizeOptionalString, normalizeOptionalString as normalizeOptionalString$1, readStringField } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
3
|
+
import { createDeferred } from "openclaw/plugin-sdk/extension-shared";
|
|
4
|
+
import { embeddedAgentLog } from "openclaw/plugin-sdk/agent-harness-runtime";
|
|
5
|
+
import { redactSensitiveText } from "openclaw/plugin-sdk/logging-core";
|
|
6
|
+
import { sanitizeTerminalText } from "openclaw/plugin-sdk/text-chunking";
|
|
7
|
+
//#region extensions/codex/src/app-server/event-projector-values.ts
|
|
8
|
+
function readNonEmptyString(record, key) {
|
|
9
|
+
return normalizeOptionalString$1(record[key]);
|
|
10
|
+
}
|
|
11
|
+
function readNonEmptyStringArray(record, key) {
|
|
12
|
+
const value = record[key];
|
|
13
|
+
if (!Array.isArray(value)) return [];
|
|
14
|
+
const entries = [];
|
|
15
|
+
for (const entry of value) {
|
|
16
|
+
const normalized = normalizeOptionalString$1(entry);
|
|
17
|
+
if (normalized) entries.push(normalized);
|
|
18
|
+
}
|
|
19
|
+
return entries;
|
|
20
|
+
}
|
|
21
|
+
function readNullableString(record, key) {
|
|
22
|
+
const value = record[key];
|
|
23
|
+
if (value === null) return null;
|
|
24
|
+
return typeof value === "string" ? value : void 0;
|
|
25
|
+
}
|
|
26
|
+
function readNonNegativeInteger(record, key) {
|
|
27
|
+
const value = asFiniteNumber(record[key]);
|
|
28
|
+
return value !== void 0 && Number.isInteger(value) && value >= 0 ? value : void 0;
|
|
29
|
+
}
|
|
30
|
+
function readCodexErrorNotificationMessage(record) {
|
|
31
|
+
const error = record.error;
|
|
32
|
+
return isJsonObject(error) ? readStringField(error, "message") : void 0;
|
|
33
|
+
}
|
|
34
|
+
function readHookOutputEntries(value) {
|
|
35
|
+
if (!Array.isArray(value)) return [];
|
|
36
|
+
return value.flatMap((entry) => {
|
|
37
|
+
if (!isJsonObject(entry)) return [];
|
|
38
|
+
const text = readStringField(entry, "text");
|
|
39
|
+
if (!text) return [];
|
|
40
|
+
const kind = readStringField(entry, "kind");
|
|
41
|
+
return [{
|
|
42
|
+
...kind ? { kind } : {},
|
|
43
|
+
text
|
|
44
|
+
}];
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
function splitPlanText(text) {
|
|
48
|
+
return text.split(/\r?\n/).map((line) => line.trim().replace(/^[-*]\s+/, "")).filter((line) => line.length > 0);
|
|
49
|
+
}
|
|
50
|
+
function extractRawAssistantText(item) {
|
|
51
|
+
const parts = (Array.isArray(item.content) ? item.content : []).flatMap((entry) => {
|
|
52
|
+
if (!isJsonObject(entry)) return [];
|
|
53
|
+
const type = readStringField(entry, "type");
|
|
54
|
+
if (type !== "output_text" && type !== "text") return [];
|
|
55
|
+
const value = readStringField(entry, "text");
|
|
56
|
+
return value === void 0 ? [] : [value];
|
|
57
|
+
});
|
|
58
|
+
return parts.length > 0 ? parts.join("").trim() : void 0;
|
|
59
|
+
}
|
|
60
|
+
function readItemString(item, key) {
|
|
61
|
+
const value = item[key];
|
|
62
|
+
return typeof value === "string" ? value : void 0;
|
|
63
|
+
}
|
|
64
|
+
function readItem(value) {
|
|
65
|
+
if (!isJsonObject(value)) return;
|
|
66
|
+
const type = typeof value.type === "string" ? value.type : void 0;
|
|
67
|
+
const id = typeof value.id === "string" ? value.id : void 0;
|
|
68
|
+
if (!type || !id) return;
|
|
69
|
+
return value;
|
|
70
|
+
}
|
|
71
|
+
//#endregion
|
|
72
|
+
//#region extensions/codex/src/app-server/event-projector-items.ts
|
|
73
|
+
function itemKind(item) {
|
|
74
|
+
switch (item.type) {
|
|
75
|
+
case "dynamicToolCall":
|
|
76
|
+
case "mcpToolCall": return "tool";
|
|
77
|
+
case "commandExecution": return "command";
|
|
78
|
+
case "fileChange": return "patch";
|
|
79
|
+
case "webSearch": return "search";
|
|
80
|
+
case "reasoning":
|
|
81
|
+
case "contextCompaction": return "analysis";
|
|
82
|
+
default: return;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function itemTitle(item) {
|
|
86
|
+
switch (item.type) {
|
|
87
|
+
case "commandExecution": return "Command";
|
|
88
|
+
case "fileChange": return "File change";
|
|
89
|
+
case "mcpToolCall": return "MCP tool";
|
|
90
|
+
case "dynamicToolCall": return "Tool";
|
|
91
|
+
case "webSearch": return "Web search";
|
|
92
|
+
case "contextCompaction": return "Context compaction";
|
|
93
|
+
case "reasoning": return "Reasoning";
|
|
94
|
+
default: return item.type;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
function itemStatus(item) {
|
|
98
|
+
const status = readItemString(item, "status");
|
|
99
|
+
if (status === "failed" || status === "error") return "failed";
|
|
100
|
+
if (status === "declined") return "blocked";
|
|
101
|
+
if (status === "inProgress" || status === "in_progress" || status === "running") return "running";
|
|
102
|
+
return "completed";
|
|
103
|
+
}
|
|
104
|
+
function unknownItemStatus(item) {
|
|
105
|
+
const status = readItemString(item, "status");
|
|
106
|
+
switch (status) {
|
|
107
|
+
case void 0:
|
|
108
|
+
case "completed":
|
|
109
|
+
case "failed":
|
|
110
|
+
case "error":
|
|
111
|
+
case "declined":
|
|
112
|
+
case "inProgress":
|
|
113
|
+
case "in_progress":
|
|
114
|
+
case "running": return;
|
|
115
|
+
default: return status;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
function auditNativeToolTerminalStatus(item) {
|
|
119
|
+
if (item.type === "imageView" || item.type === "sleep") return "completed";
|
|
120
|
+
const status = readItemString(item, "status");
|
|
121
|
+
if (status === "completed") return "completed";
|
|
122
|
+
if (status === "failed" || status === "error") return "failed";
|
|
123
|
+
if (status === "declined") return "blocked";
|
|
124
|
+
return "unknown";
|
|
125
|
+
}
|
|
126
|
+
function auditNativeToolUnfinishedStatus(item) {
|
|
127
|
+
return item.type === "webSearch" || item.type === "imageGeneration" ? "unknown" : "failed";
|
|
128
|
+
}
|
|
129
|
+
function isNonSuccessItemStatus(status) {
|
|
130
|
+
return status === "failed" || status === "blocked";
|
|
131
|
+
}
|
|
132
|
+
function itemName(item) {
|
|
133
|
+
if (item.type === "dynamicToolCall" && typeof item.tool === "string") return item.tool;
|
|
134
|
+
if (item.type === "mcpToolCall" && typeof item.tool === "string") {
|
|
135
|
+
const server = typeof item.server === "string" ? item.server : void 0;
|
|
136
|
+
return server ? `${server}.${item.tool}` : item.tool;
|
|
137
|
+
}
|
|
138
|
+
if (item.type === "commandExecution") return "bash";
|
|
139
|
+
if (item.type === "fileChange") return "apply_patch";
|
|
140
|
+
if (item.type === "webSearch") return "web_search";
|
|
141
|
+
}
|
|
142
|
+
function auditNativeToolName(item) {
|
|
143
|
+
if (item.type === "dynamicToolCall") return;
|
|
144
|
+
const progressName = itemName(item);
|
|
145
|
+
if (progressName) return progressName;
|
|
146
|
+
if (item.type === "collabAgentToolCall") return typeof item.tool === "string" && item.tool.trim() ? `collab.${item.tool.trim()}` : "collab_agent";
|
|
147
|
+
if (item.type === "imageGeneration") return "image_generation";
|
|
148
|
+
if (item.type === "imageView") return "image_view";
|
|
149
|
+
if (item.type === "sleep") return "sleep";
|
|
150
|
+
}
|
|
151
|
+
function isSideEffectingNativeToolItem(item) {
|
|
152
|
+
return itemStatus(item) !== "blocked" && (isMutatingNativeToolItem(item) || item.type === "mcpToolCall");
|
|
153
|
+
}
|
|
154
|
+
function shouldSynthesizeToolProgressForItem(item) {
|
|
155
|
+
switch (item.type) {
|
|
156
|
+
case "commandExecution":
|
|
157
|
+
case "fileChange":
|
|
158
|
+
case "webSearch":
|
|
159
|
+
case "mcpToolCall": return true;
|
|
160
|
+
default: return false;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
function shouldRecordNativeToolTranscript(item) {
|
|
164
|
+
return shouldSynthesizeToolProgressForItem(item);
|
|
165
|
+
}
|
|
166
|
+
function isMutatingNativeToolItem(item) {
|
|
167
|
+
if (item.type === "commandExecution") return true;
|
|
168
|
+
return item.type === "fileChange" || item.type === "collabAgentToolCall" || item.type === "imageGeneration";
|
|
169
|
+
}
|
|
170
|
+
function shouldClearTerminalPresentationForNativeItem(item) {
|
|
171
|
+
switch (item.type) {
|
|
172
|
+
case "collabAgentToolCall":
|
|
173
|
+
case "commandExecution":
|
|
174
|
+
case "fileChange":
|
|
175
|
+
case "imageGeneration":
|
|
176
|
+
case "imageView":
|
|
177
|
+
case "mcpToolCall":
|
|
178
|
+
case "webSearch": return true;
|
|
179
|
+
default: return false;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
//#endregion
|
|
183
|
+
//#region extensions/codex/src/app-server/notification-correlation.ts
|
|
184
|
+
/**
|
|
185
|
+
* Correlates Codex app-server notifications with the active thread/turn so
|
|
186
|
+
* projectors can ignore global or stale events without losing diagnostics.
|
|
187
|
+
*/
|
|
188
|
+
/** Returns true when a notification payload belongs to the exact active thread and turn. */
|
|
189
|
+
function isCodexNotificationForTurn(value, threadId, turnId) {
|
|
190
|
+
if (!isJsonObject(value)) return false;
|
|
191
|
+
return readCodexNotificationThreadId(value) === threadId && readCodexNotificationTurnId(value) === turnId;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Reads a thread id from canonical top-level or nested thread payloads.
|
|
195
|
+
* The generated v2 schemas require top-level `threadId` on turn/item-scoped
|
|
196
|
+
* notifications and define `Turn` without one, so `turn.threadId` is not a
|
|
197
|
+
* wire shape and is deliberately not read here.
|
|
198
|
+
*/
|
|
199
|
+
function readCodexNotificationThreadId(record) {
|
|
200
|
+
const thread = isJsonObject(record.thread) ? record.thread : void 0;
|
|
201
|
+
return normalizeOptionalString(record.threadId) ?? (thread ? normalizeOptionalString(thread.id) : void 0);
|
|
202
|
+
}
|
|
203
|
+
/** Reads a turn id from either top-level notification params or nested turn payloads. */
|
|
204
|
+
function readCodexNotificationTurnId(record) {
|
|
205
|
+
return readNestedTurnId(record) ?? normalizeOptionalString(record.turnId);
|
|
206
|
+
}
|
|
207
|
+
function readNestedTurnId(record) {
|
|
208
|
+
const turn = record.turn;
|
|
209
|
+
return isJsonObject(turn) ? normalizeOptionalString(turn.id) : void 0;
|
|
210
|
+
}
|
|
211
|
+
//#endregion
|
|
212
|
+
//#region extensions/codex/src/app-server/event-projector-diagnostics.ts
|
|
213
|
+
function redactCodexEventKind(method) {
|
|
214
|
+
return redactSensitiveText(sanitizeTerminalText(method));
|
|
215
|
+
}
|
|
216
|
+
var CodexProjectionDiagnostics = class {
|
|
217
|
+
constructor(threadId, turnId) {
|
|
218
|
+
this.threadId = threadId;
|
|
219
|
+
this.turnId = turnId;
|
|
220
|
+
this.warningKeys = /* @__PURE__ */ new Set();
|
|
221
|
+
}
|
|
222
|
+
warnUnknownItemStatus(item) {
|
|
223
|
+
if (!item) return;
|
|
224
|
+
const status = unknownItemStatus(item);
|
|
225
|
+
if (!status) return;
|
|
226
|
+
const safeStatus = redactCodexEventKind(status);
|
|
227
|
+
const safeItemType = redactCodexEventKind(item.type);
|
|
228
|
+
this.warnOnce(JSON.stringify([
|
|
229
|
+
"status",
|
|
230
|
+
item.type,
|
|
231
|
+
status
|
|
232
|
+
]), "codex app-server item reported unknown status; continuing projection", {
|
|
233
|
+
itemId: item.id,
|
|
234
|
+
itemType: safeItemType,
|
|
235
|
+
status: safeStatus
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
warnUnknownEvent(notification, params) {
|
|
239
|
+
const notificationThreadId = readCodexNotificationThreadId(params);
|
|
240
|
+
const notificationTurnId = readCodexNotificationTurnId(params);
|
|
241
|
+
const eventKind = redactCodexEventKind(notification.method);
|
|
242
|
+
this.warnOnce(JSON.stringify(["method", notification.method]), `codex app-server projector received unknown event kind; continuing: ${eventKind}`, {
|
|
243
|
+
eventKind,
|
|
244
|
+
activeThreadId: this.threadId,
|
|
245
|
+
activeTurnId: this.turnId,
|
|
246
|
+
threadId: notificationThreadId,
|
|
247
|
+
turnId: notificationTurnId,
|
|
248
|
+
matchesActiveThread: notificationThreadId === this.threadId,
|
|
249
|
+
matchesActiveTurn: notificationTurnId === this.turnId
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
warnOnce(key, message, context) {
|
|
253
|
+
if (this.warningKeys.has(key)) return;
|
|
254
|
+
this.warningKeys.add(key);
|
|
255
|
+
embeddedAgentLog.warn(message, context);
|
|
256
|
+
}
|
|
257
|
+
};
|
|
258
|
+
//#endregion
|
|
259
|
+
//#region extensions/codex/src/app-server/turn-router.ts
|
|
260
|
+
/** Keyed routing for all turn traffic on one shared Codex app-server client. */
|
|
261
|
+
const DEFAULT_PREBIND_NOTIFICATION_LIMIT = 256;
|
|
262
|
+
const CODEX_APP_SERVER_NATIVE_TURN_WAIT_TIMEOUT_MS = 3e4;
|
|
263
|
+
const routers = /* @__PURE__ */ new WeakMap();
|
|
264
|
+
/** Returns the sole router installed on a physical app-server client. */
|
|
265
|
+
function getCodexAppServerTurnRouter(client) {
|
|
266
|
+
const existing = routers.get(client);
|
|
267
|
+
if (existing) return existing;
|
|
268
|
+
const router = new ClientTurnRouter(client);
|
|
269
|
+
routers.set(client, router);
|
|
270
|
+
return router;
|
|
271
|
+
}
|
|
272
|
+
var ClientTurnRouter = class {
|
|
273
|
+
constructor(client) {
|
|
274
|
+
this.routes = /* @__PURE__ */ new Map();
|
|
275
|
+
this.nativeTurnCompletionWatchers = /* @__PURE__ */ new Map();
|
|
276
|
+
this.disposed = false;
|
|
277
|
+
client.addNotificationHandler((notification) => this.routeNotification(notification));
|
|
278
|
+
client.addRequestHandler((request, signal) => this.routeRequest(request, signal));
|
|
279
|
+
client.addCloseHandler((closedClient) => {
|
|
280
|
+
this.dispose(closedClient.getCloseError());
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
reserveThread(options) {
|
|
284
|
+
this.assertActive();
|
|
285
|
+
const threadId = requireId(options.threadId, "thread id");
|
|
286
|
+
if (this.routes.has(threadId)) throw new Error(`codex app-server thread route already reserved: ${threadId}`);
|
|
287
|
+
const route = {
|
|
288
|
+
threadId,
|
|
289
|
+
controller: new AbortController(),
|
|
290
|
+
ended: createDeferred(),
|
|
291
|
+
activated: createDeferred(),
|
|
292
|
+
gate: "open",
|
|
293
|
+
pending: [],
|
|
294
|
+
notificationTail: Promise.resolve(),
|
|
295
|
+
completedNativeTurnIds: /* @__PURE__ */ new Set(),
|
|
296
|
+
ignoredTurnNotificationKeys: /* @__PURE__ */ new Set()
|
|
297
|
+
};
|
|
298
|
+
this.routes.set(threadId, route);
|
|
299
|
+
if (options.onNotification || options.onRequest) this.activateNow(route, options);
|
|
300
|
+
const releaseOn = options.releaseOn;
|
|
301
|
+
if (releaseOn) {
|
|
302
|
+
const release = () => this.release(route, abortReason(releaseOn));
|
|
303
|
+
releaseOn.addEventListener("abort", release, { once: true });
|
|
304
|
+
route.detachReleaseOn = () => releaseOn.removeEventListener("abort", release);
|
|
305
|
+
if (releaseOn.aborted) release();
|
|
306
|
+
}
|
|
307
|
+
return {
|
|
308
|
+
threadId,
|
|
309
|
+
signal: route.controller.signal,
|
|
310
|
+
get observedNativeTurnId() {
|
|
311
|
+
return route.observedNativeTurn?.id;
|
|
312
|
+
},
|
|
313
|
+
activate: (handlers) => this.activate(route, handlers),
|
|
314
|
+
armTurn: () => this.armTurn(route),
|
|
315
|
+
bindTurn: (turnId) => this.bindTurn(route, turnId),
|
|
316
|
+
cancelTurn: () => this.cancelTurn(route),
|
|
317
|
+
drain: () => this.drainNotifications(route),
|
|
318
|
+
release: () => this.release(route)
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
watchNativeTurnCompletion(options) {
|
|
322
|
+
this.assertActive();
|
|
323
|
+
const threadId = requireId(options.threadId, "thread id");
|
|
324
|
+
const turnId = requireId(options.turnId, "turn id");
|
|
325
|
+
if (options.signal?.aborted) return {
|
|
326
|
+
completion: Promise.resolve(false),
|
|
327
|
+
cancel: () => {}
|
|
328
|
+
};
|
|
329
|
+
if (this.routes.get(threadId)?.completedNativeTurnIds.delete(turnId)) return {
|
|
330
|
+
completion: Promise.resolve(true),
|
|
331
|
+
cancel: () => {}
|
|
332
|
+
};
|
|
333
|
+
const { promise: completion, resolve: settle } = createDeferred();
|
|
334
|
+
const watchers = this.nativeTurnCompletionWatchers.get(threadId) ?? /* @__PURE__ */ new Set();
|
|
335
|
+
this.nativeTurnCompletionWatchers.set(threadId, watchers);
|
|
336
|
+
let settled = false;
|
|
337
|
+
const finish = (completed) => {
|
|
338
|
+
if (settled) return;
|
|
339
|
+
settled = true;
|
|
340
|
+
watchers.delete(watcher);
|
|
341
|
+
if (watchers.size === 0) this.nativeTurnCompletionWatchers.delete(threadId);
|
|
342
|
+
clearTimeout(timeout);
|
|
343
|
+
options.signal?.removeEventListener("abort", abort);
|
|
344
|
+
settle(completed);
|
|
345
|
+
};
|
|
346
|
+
const watcher = {
|
|
347
|
+
turnId,
|
|
348
|
+
finish
|
|
349
|
+
};
|
|
350
|
+
watchers.add(watcher);
|
|
351
|
+
const timeout = setTimeout(() => finish(false), Math.max(1, options.timeoutMs));
|
|
352
|
+
timeout.unref?.();
|
|
353
|
+
const abort = () => finish(false);
|
|
354
|
+
options.signal?.addEventListener("abort", abort, { once: true });
|
|
355
|
+
return {
|
|
356
|
+
completion,
|
|
357
|
+
cancel: () => finish(false)
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
dispose(cause) {
|
|
361
|
+
if (this.disposed) return;
|
|
362
|
+
this.disposed = true;
|
|
363
|
+
const closeError = cause ? new Error("codex app-server turn router closed", { cause }) : /* @__PURE__ */ new Error("codex app-server turn router closed");
|
|
364
|
+
for (const route of this.routes.values()) this.release(route, closeError);
|
|
365
|
+
for (const watchers of this.nativeTurnCompletionWatchers.values()) for (const watcher of watchers) watcher.finish(false);
|
|
366
|
+
}
|
|
367
|
+
async activate(route, handlers) {
|
|
368
|
+
this.assertRoute(route);
|
|
369
|
+
this.activateNow(route, handlers);
|
|
370
|
+
await this.waitForNotifications(route);
|
|
371
|
+
this.assertRoute(route);
|
|
372
|
+
}
|
|
373
|
+
activateNow(route, handlers) {
|
|
374
|
+
if (route.handlers) throw new Error(`codex app-server thread route already activated: ${route.threadId}`);
|
|
375
|
+
this.assertRoute(route);
|
|
376
|
+
if (!handlers.onNotification && !handlers.onRequest) throw new Error("codex app-server thread route requires a notification or request handler");
|
|
377
|
+
route.handlers = handlers;
|
|
378
|
+
if (!handlers.onNotification) route.pending.length = 0;
|
|
379
|
+
else if (route.gate !== "armed") this.flushNotifications(route);
|
|
380
|
+
route.activated.resolve();
|
|
381
|
+
}
|
|
382
|
+
armTurn(route) {
|
|
383
|
+
this.assertRoute(route);
|
|
384
|
+
if (route.gate !== "open") throw new Error(`codex app-server thread route cannot arm from ${route.gate}`);
|
|
385
|
+
route.gate = "armed";
|
|
386
|
+
route.ignoredTurnNotificationKeys.clear();
|
|
387
|
+
route.completedNativeTurnIds.clear();
|
|
388
|
+
if (route.observedNativeTurn?.completed) route.observedNativeTurn = void 0;
|
|
389
|
+
route.binding = createDeferred();
|
|
390
|
+
}
|
|
391
|
+
async cancelTurn(route) {
|
|
392
|
+
if (route.released || route.gate !== "armed") return;
|
|
393
|
+
route.gate = "open";
|
|
394
|
+
route.binding?.resolve();
|
|
395
|
+
route.binding = void 0;
|
|
396
|
+
this.flushNotifications(route);
|
|
397
|
+
await this.waitForNotifications(route);
|
|
398
|
+
this.assertRoute(route);
|
|
399
|
+
}
|
|
400
|
+
async bindTurn(route, turnIdInput) {
|
|
401
|
+
this.assertRoute(route);
|
|
402
|
+
if (!route.handlers) throw new Error("codex app-server thread route must be activated before binding a turn");
|
|
403
|
+
if (route.gate !== "armed") throw new Error(`codex app-server thread route cannot bind from ${route.gate}`);
|
|
404
|
+
const turnId = requireId(turnIdInput, "turn id");
|
|
405
|
+
route.gate = "bound";
|
|
406
|
+
route.turnId = turnId;
|
|
407
|
+
this.flushNotifications(route);
|
|
408
|
+
route.binding?.resolve();
|
|
409
|
+
await this.waitForNotifications(route);
|
|
410
|
+
this.assertRoute(route);
|
|
411
|
+
}
|
|
412
|
+
routeNotification(notification) {
|
|
413
|
+
if (this.disposed) return;
|
|
414
|
+
const scope = readScope(notification.params);
|
|
415
|
+
const watchers = scope.threadId ? this.nativeTurnCompletionWatchers.get(scope.threadId) : void 0;
|
|
416
|
+
const route = scope.threadId ? this.routes.get(scope.threadId) : void 0;
|
|
417
|
+
if (!watchers && !route) return;
|
|
418
|
+
if (scope.turnId && watchers) {
|
|
419
|
+
for (const watcher of watchers) if (watcher.turnId === scope.turnId && notification.method === "turn/completed") watcher.finish(true);
|
|
420
|
+
}
|
|
421
|
+
if (!route) return;
|
|
422
|
+
const routeScope = {
|
|
423
|
+
threadId: route.threadId,
|
|
424
|
+
...scope.turnId ? { turnId: scope.turnId } : {}
|
|
425
|
+
};
|
|
426
|
+
const receivedAtMs = Date.now();
|
|
427
|
+
if (route.gate !== "bound" && scope.turnId) {
|
|
428
|
+
if (notification.method === "turn/started") route.observedNativeTurn = {
|
|
429
|
+
id: scope.turnId,
|
|
430
|
+
completed: false
|
|
431
|
+
};
|
|
432
|
+
else if (notification.method === "turn/completed") {
|
|
433
|
+
route.completedNativeTurnIds.add(scope.turnId);
|
|
434
|
+
if (!route.observedNativeTurn || route.observedNativeTurn.completed || route.observedNativeTurn.id === scope.turnId) route.observedNativeTurn = {
|
|
435
|
+
id: scope.turnId,
|
|
436
|
+
completed: true
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
if (!route.handlers) {
|
|
441
|
+
this.bufferNotification(route, notification, routeScope, receivedAtMs);
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
const handler = route.handlers.onNotification;
|
|
445
|
+
if (!handler) return;
|
|
446
|
+
if (route.gate === "bound" && scope.turnId && scope.turnId !== route.turnId) {
|
|
447
|
+
this.warnDroppedStaleTurnNotification(route, notification, routeScope);
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
if (route.gate === "armed") {
|
|
451
|
+
this.bufferNotification(route, notification, routeScope, receivedAtMs);
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
route.handlers.onNotificationReceived?.(notification, routeScope, receivedAtMs);
|
|
455
|
+
this.enqueueNotification(route, handler, notification, routeScope);
|
|
456
|
+
return route.notificationTail;
|
|
457
|
+
}
|
|
458
|
+
async routeRequest(request, signal = new AbortController().signal) {
|
|
459
|
+
if (this.disposed || signal.aborted) return;
|
|
460
|
+
const scope = readScope(request.params);
|
|
461
|
+
if (!scope.threadId) return;
|
|
462
|
+
const route = this.routes.get(scope.threadId);
|
|
463
|
+
if (!route) return;
|
|
464
|
+
const requestSignal = AbortSignal.any([signal, route.controller.signal]);
|
|
465
|
+
if (!route.handlers && !await waitForPromiseOrAbort(route.activated.promise, requestSignal)) return;
|
|
466
|
+
if (requestSignal.aborted || !route.handlers) return;
|
|
467
|
+
const handler = route.handlers.onRequest;
|
|
468
|
+
if (!handler) return;
|
|
469
|
+
while (route.gate === "armed") {
|
|
470
|
+
const binding = route.binding?.promise;
|
|
471
|
+
if (!binding || !await waitForPromiseOrAbort(binding, requestSignal)) return;
|
|
472
|
+
if (requestSignal.aborted) return;
|
|
473
|
+
}
|
|
474
|
+
if (route.gate === "bound" && scope.turnId && scope.turnId !== route.turnId) return;
|
|
475
|
+
if (!await waitForPromiseOrAbort(this.waitForNotifications(route), requestSignal)) return;
|
|
476
|
+
if (requestSignal.aborted) return;
|
|
477
|
+
try {
|
|
478
|
+
const result = await handler(request, {
|
|
479
|
+
threadId: scope.threadId,
|
|
480
|
+
...scope.turnId ? { turnId: scope.turnId } : {}
|
|
481
|
+
}, requestSignal);
|
|
482
|
+
return requestSignal.aborted ? void 0 : result;
|
|
483
|
+
} catch (error) {
|
|
484
|
+
if (requestSignal.aborted) return;
|
|
485
|
+
throw error;
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
flushNotifications(route) {
|
|
489
|
+
const handler = route.handlers?.onNotification;
|
|
490
|
+
if (!handler) return;
|
|
491
|
+
for (const pending of route.pending.splice(0)) {
|
|
492
|
+
if (route.gate === "bound" && pending.scope.turnId && pending.scope.turnId !== route.turnId) {
|
|
493
|
+
this.warnDroppedStaleTurnNotification(route, pending.notification, pending.scope);
|
|
494
|
+
continue;
|
|
495
|
+
}
|
|
496
|
+
route.handlers?.onNotificationReceived?.(pending.notification, pending.scope, pending.receivedAtMs);
|
|
497
|
+
this.enqueueNotification(route, handler, pending.notification, pending.scope);
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
warnDroppedStaleTurnNotification(route, notification, scope) {
|
|
501
|
+
if (notification.method === "turn/completed" || !scope.turnId || !route.turnId) return;
|
|
502
|
+
const eventKind = redactCodexEventKind(notification.method);
|
|
503
|
+
const key = JSON.stringify([notification.method, scope.turnId]);
|
|
504
|
+
if (route.ignoredTurnNotificationKeys.has(key)) return;
|
|
505
|
+
route.ignoredTurnNotificationKeys.add(key);
|
|
506
|
+
embeddedAgentLog.warn("codex app-server notification ignored for inactive turn", {
|
|
507
|
+
eventKind,
|
|
508
|
+
activeThreadId: route.threadId,
|
|
509
|
+
activeTurnId: route.turnId,
|
|
510
|
+
threadId: scope.threadId,
|
|
511
|
+
turnId: scope.turnId,
|
|
512
|
+
matchesActiveThread: true,
|
|
513
|
+
matchesActiveTurn: false
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
bufferNotification(route, notification, scope, receivedAtMs) {
|
|
517
|
+
if (route.pending.length < DEFAULT_PREBIND_NOTIFICATION_LIMIT) {
|
|
518
|
+
route.pending.push({
|
|
519
|
+
notification,
|
|
520
|
+
receivedAtMs,
|
|
521
|
+
scope
|
|
522
|
+
});
|
|
523
|
+
return;
|
|
524
|
+
}
|
|
525
|
+
const error = /* @__PURE__ */ new Error(`codex app-server pre-bind notification buffer exceeded ${DEFAULT_PREBIND_NOTIFICATION_LIMIT} entries for thread ${route.threadId}`);
|
|
526
|
+
embeddedAgentLog.warn(error.message);
|
|
527
|
+
this.release(route, error);
|
|
528
|
+
}
|
|
529
|
+
enqueueNotification(route, handler, notification, scope) {
|
|
530
|
+
if (route.released) return;
|
|
531
|
+
route.notificationTail = route.notificationTail.then(() => handler(notification, scope)).catch((error) => {
|
|
532
|
+
if (!route.released) embeddedAgentLog.warn("codex app-server keyed notification handler failed", {
|
|
533
|
+
method: notification.method,
|
|
534
|
+
threadId: route.threadId,
|
|
535
|
+
turnId: route.turnId,
|
|
536
|
+
error
|
|
537
|
+
});
|
|
538
|
+
});
|
|
539
|
+
}
|
|
540
|
+
async waitForNotifications(route) {
|
|
541
|
+
await Promise.race([route.notificationTail, route.ended.promise]);
|
|
542
|
+
}
|
|
543
|
+
async drainNotifications(route) {
|
|
544
|
+
await route.notificationTail;
|
|
545
|
+
}
|
|
546
|
+
release(route, error = /* @__PURE__ */ new Error("codex app-server thread route is released")) {
|
|
547
|
+
if (route.released) return;
|
|
548
|
+
route.released = error;
|
|
549
|
+
route.pending.length = 0;
|
|
550
|
+
route.ended.resolve();
|
|
551
|
+
route.activated.resolve();
|
|
552
|
+
route.binding?.resolve();
|
|
553
|
+
route.detachReleaseOn?.();
|
|
554
|
+
route.controller.abort(error);
|
|
555
|
+
if (this.routes.get(route.threadId) === route) this.routes.delete(route.threadId);
|
|
556
|
+
}
|
|
557
|
+
assertActive() {
|
|
558
|
+
if (this.disposed) throw new Error("codex app-server turn router is closed");
|
|
559
|
+
}
|
|
560
|
+
assertRoute(route) {
|
|
561
|
+
if (route.released) throw route.released;
|
|
562
|
+
}
|
|
563
|
+
};
|
|
564
|
+
async function waitForPromiseOrAbort(promise, signal) {
|
|
565
|
+
if (signal.aborted) return false;
|
|
566
|
+
let removeAbort;
|
|
567
|
+
try {
|
|
568
|
+
return await Promise.race([promise.then(() => true), new Promise((resolve) => {
|
|
569
|
+
const onAbort = () => resolve(false);
|
|
570
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
571
|
+
removeAbort = () => signal.removeEventListener("abort", onAbort);
|
|
572
|
+
if (signal.aborted) onAbort();
|
|
573
|
+
})]);
|
|
574
|
+
} finally {
|
|
575
|
+
removeAbort?.();
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
function abortReason(signal) {
|
|
579
|
+
return signal.reason instanceof Error ? signal.reason : new Error(String(signal.reason ?? "codex app-server thread route aborted"));
|
|
580
|
+
}
|
|
581
|
+
function readScope(value) {
|
|
582
|
+
if (!isJsonObject(value)) return {};
|
|
583
|
+
const threadId = readCodexNotificationThreadId(value);
|
|
584
|
+
const turnId = readCodexNotificationTurnId(value);
|
|
585
|
+
return {
|
|
586
|
+
...threadId ? { threadId } : {},
|
|
587
|
+
...turnId ? { turnId } : {}
|
|
588
|
+
};
|
|
589
|
+
}
|
|
590
|
+
function requireId(value, label) {
|
|
591
|
+
const normalized = value.trim();
|
|
592
|
+
if (!normalized) throw new Error(`codex app-server ${label} must not be empty`);
|
|
593
|
+
return normalized;
|
|
594
|
+
}
|
|
595
|
+
//#endregion
|
|
596
|
+
//#region extensions/codex/src/app-server/attempt-client-cleanup.ts
|
|
597
|
+
/**
|
|
598
|
+
* Best-effort cleanup helpers for Codex app-server startup attempts and turns.
|
|
599
|
+
*/
|
|
600
|
+
/** Timeout for best-effort app-server turn interruption during cleanup. */
|
|
601
|
+
const CODEX_APP_SERVER_INTERRUPT_TIMEOUT_MS = 5e3;
|
|
602
|
+
/** Timeout for best-effort thread unsubscribe during cleanup. */
|
|
603
|
+
const CODEX_APP_SERVER_UNSUBSCRIBE_TIMEOUT_MS = 5e3;
|
|
604
|
+
const CODEX_NO_ACTIVE_TURN_ERROR_CODE = -32600;
|
|
605
|
+
const CODEX_NO_ACTIVE_TURN_ERROR_MESSAGE = "no active turn to interrupt";
|
|
606
|
+
/** Identifies Codex's exact proof that an interrupt target already finished. */
|
|
607
|
+
function isCodexAlreadyTerminalInterruptError(error) {
|
|
608
|
+
return error instanceof CodexAppServerRpcError && error.code === CODEX_NO_ACTIVE_TURN_ERROR_CODE && error.message === CODEX_NO_ACTIVE_TURN_ERROR_MESSAGE;
|
|
609
|
+
}
|
|
610
|
+
/** Raised when a thread subscription may be live on a client OpenClaw no longer controls. */
|
|
611
|
+
var CodexAppServerUnsafeSubscriptionError = class extends Error {
|
|
612
|
+
constructor(message, options) {
|
|
613
|
+
super(message, options);
|
|
614
|
+
this.name = "CodexAppServerUnsafeSubscriptionError";
|
|
615
|
+
}
|
|
616
|
+
};
|
|
617
|
+
function isCodexAppServerUnsafeSubscriptionError(error) {
|
|
618
|
+
return error instanceof CodexAppServerUnsafeSubscriptionError;
|
|
619
|
+
}
|
|
620
|
+
/** Asserts Codex resumed the exact thread this attempt subscribed to. */
|
|
621
|
+
function assertCodexThreadResumeSubscription(requestedThreadId, returnedThreadId) {
|
|
622
|
+
if (returnedThreadId !== requestedThreadId) throw new CodexAppServerUnsafeSubscriptionError(`Codex thread/resume returned ${returnedThreadId} for ${requestedThreadId}`);
|
|
623
|
+
}
|
|
624
|
+
async function closeClientAndWaitIfAvailable(client) {
|
|
625
|
+
const closeable = client;
|
|
626
|
+
if (typeof closeable.closeAndWait === "function") {
|
|
627
|
+
await closeable.closeAndWait();
|
|
628
|
+
return;
|
|
629
|
+
}
|
|
630
|
+
closeable.close?.();
|
|
631
|
+
}
|
|
632
|
+
async function closeCodexStartupClientBestEffort(client) {
|
|
633
|
+
if (!client) return;
|
|
634
|
+
const retiredSharedClient = retireSharedCodexAppServerClientIfCurrent(client);
|
|
635
|
+
if (!retiredSharedClient || retiredSharedClient.closed) await closeClientAndWaitIfAvailable(client);
|
|
636
|
+
}
|
|
637
|
+
/** Retires an unsafe turn client without replacing an already-authoritative failure. */
|
|
638
|
+
async function retireUnsafeCodexTurnClientBestEffort(client, operation) {
|
|
639
|
+
try {
|
|
640
|
+
await closeCodexStartupClientBestEffort(client);
|
|
641
|
+
} catch (error) {
|
|
642
|
+
embeddedAgentLog.debug("codex app-server unsafe turn client retirement failed", {
|
|
643
|
+
operation,
|
|
644
|
+
error
|
|
645
|
+
});
|
|
646
|
+
try {
|
|
647
|
+
client.close();
|
|
648
|
+
} catch (closeError) {
|
|
649
|
+
embeddedAgentLog.debug("codex app-server unsafe turn client close failed", {
|
|
650
|
+
operation,
|
|
651
|
+
error: closeError
|
|
652
|
+
});
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
/** Sends a bounded turn interrupt and waits for Codex to confirm terminal abort handling. */
|
|
657
|
+
async function interruptCodexTurnAndWaitBestEffort(client, params) {
|
|
658
|
+
const timeoutMs = params.timeoutMs && Number.isFinite(params.timeoutMs) && params.timeoutMs > 0 ? params.timeoutMs : CODEX_APP_SERVER_INTERRUPT_TIMEOUT_MS;
|
|
659
|
+
const requestParams = {
|
|
660
|
+
threadId: params.threadId,
|
|
661
|
+
turnId: params.turnId
|
|
662
|
+
};
|
|
663
|
+
let completion;
|
|
664
|
+
try {
|
|
665
|
+
completion = params.turnId ? getCodexAppServerTurnRouter(client).watchNativeTurnCompletion({
|
|
666
|
+
threadId: params.threadId,
|
|
667
|
+
turnId: params.turnId,
|
|
668
|
+
timeoutMs
|
|
669
|
+
}) : void 0;
|
|
670
|
+
await client.request("turn/interrupt", requestParams, { timeoutMs });
|
|
671
|
+
return completion ? await completion.completion : true;
|
|
672
|
+
} catch (error) {
|
|
673
|
+
if (isCodexAlreadyTerminalInterruptError(error)) return true;
|
|
674
|
+
embeddedAgentLog.debug("codex app-server turn interrupt failed during abort", { error });
|
|
675
|
+
return false;
|
|
676
|
+
} finally {
|
|
677
|
+
completion?.cancel();
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
/** Unsubscribes from a thread while swallowing cleanup-only failures. */
|
|
681
|
+
async function unsubscribeCodexThreadBestEffort(client, params) {
|
|
682
|
+
try {
|
|
683
|
+
await unsubscribeCodexAppServerLiveThread(client, params.threadId, params.timeoutMs);
|
|
684
|
+
return true;
|
|
685
|
+
} catch (error) {
|
|
686
|
+
embeddedAgentLog.debug("codex app-server thread unsubscribe cleanup failed", {
|
|
687
|
+
threadId: params.threadId,
|
|
688
|
+
error
|
|
689
|
+
});
|
|
690
|
+
return false;
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
/**
|
|
694
|
+
* Retires the shared client after a timed-out turn so later runs do not reuse a
|
|
695
|
+
* potentially wedged app-server connection.
|
|
696
|
+
*/
|
|
697
|
+
async function retireCodexAppServerClientAfterTimedOutTurn(client, params) {
|
|
698
|
+
const retiredSharedClient = retireSharedCodexAppServerClientIfCurrent(client, { failActiveLeases: params.suspectPhysicalClient });
|
|
699
|
+
const detachedSharedClient = Boolean(retiredSharedClient);
|
|
700
|
+
if (!(params.suspectPhysicalClient && (retiredSharedClient?.closed ?? false))) {
|
|
701
|
+
await interruptCodexTurnAndWaitBestEffort(client, {
|
|
702
|
+
threadId: params.threadId,
|
|
703
|
+
turnId: params.turnId,
|
|
704
|
+
timeoutMs: CODEX_APP_SERVER_INTERRUPT_TIMEOUT_MS
|
|
705
|
+
});
|
|
706
|
+
await unsubscribeCodexThreadBestEffort(client, {
|
|
707
|
+
threadId: params.threadId,
|
|
708
|
+
timeoutMs: CODEX_APP_SERVER_UNSUBSCRIBE_TIMEOUT_MS
|
|
709
|
+
});
|
|
710
|
+
}
|
|
711
|
+
let closedClient = retiredSharedClient?.closed ?? false;
|
|
712
|
+
if (!detachedSharedClient) {
|
|
713
|
+
const close = client.close;
|
|
714
|
+
if (typeof close === "function") try {
|
|
715
|
+
close.call(client);
|
|
716
|
+
closedClient = true;
|
|
717
|
+
} catch (error) {
|
|
718
|
+
embeddedAgentLog.debug("codex app-server client close failed during timeout cleanup", {
|
|
719
|
+
threadId: params.threadId,
|
|
720
|
+
turnId: params.turnId,
|
|
721
|
+
error
|
|
722
|
+
});
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
embeddedAgentLog.warn("codex app-server client retired after timed-out turn", {
|
|
726
|
+
threadId: params.threadId,
|
|
727
|
+
turnId: params.turnId,
|
|
728
|
+
reason: params.reason,
|
|
729
|
+
detachedSharedClient,
|
|
730
|
+
closedClient,
|
|
731
|
+
activeSharedClientLeases: retiredSharedClient?.activeLeases ?? 0
|
|
732
|
+
});
|
|
733
|
+
}
|
|
734
|
+
//#endregion
|
|
735
|
+
export { shouldSynthesizeToolProgressForItem as A, readNullableString as B, isSideEffectingNativeToolItem as C, itemTitle as D, itemStatus as E, readItem as F, readItemString as I, readNonEmptyString as L, normalizeOptionalString$1 as M, readCodexErrorNotificationMessage as N, shouldClearTerminalPresentationForNativeItem as O, readHookOutputEntries as P, readNonEmptyStringArray as R, isNonSuccessItemStatus as S, itemName as T, splitPlanText as V, readCodexNotificationTurnId as _, closeCodexStartupClientBestEffort as a, auditNativeToolUnfinishedStatus as b, isCodexAppServerUnsafeSubscriptionError as c, unsubscribeCodexThreadBestEffort as d, CODEX_APP_SERVER_NATIVE_TURN_WAIT_TIMEOUT_MS as f, readCodexNotificationThreadId as g, isCodexNotificationForTurn as h, assertCodexThreadResumeSubscription as i, extractRawAssistantText as j, shouldRecordNativeToolTranscript as k, retireCodexAppServerClientAfterTimedOutTurn as l, CodexProjectionDiagnostics as m, CODEX_APP_SERVER_UNSUBSCRIBE_TIMEOUT_MS as n, interruptCodexTurnAndWaitBestEffort as o, getCodexAppServerTurnRouter as p, CodexAppServerUnsafeSubscriptionError as r, isCodexAlreadyTerminalInterruptError as s, CODEX_APP_SERVER_INTERRUPT_TIMEOUT_MS as t, retireUnsafeCodexTurnClientBestEffort as u, auditNativeToolName as v, itemKind as w, isMutatingNativeToolItem as x, auditNativeToolTerminalStatus as y, readNonNegativeInteger as z };
|