@openclaw/codex 2026.7.2-beta.7 → 2026.8.1-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.
Files changed (55) hide show
  1. package/README.md +2 -0
  2. package/dist/{app-server-policy-BSaO2JIT.js → app-server-policy-zcBDc1JU.js} +1 -1
  3. package/dist/attempt-client-cleanup-DMLXUfa8.js +735 -0
  4. package/dist/{notification-correlation-CU6_AnOb.js → binding-connection-I5Bo-AsX.js} +3 -37
  5. package/dist/{bounded-turn-D2ZAgZVz.js → bounded-turn-qgZmzy_4.js} +50 -49
  6. package/dist/{capabilities-BFDTVYr4.js → capabilities-CybGI1oz.js} +8 -2
  7. package/dist/cli-metadata.js +1 -1
  8. package/dist/{command-formatters-C336eRVS.js → command-formatters-B1AEz8ZA.js} +18 -18
  9. package/dist/{command-handlers-DAHUSry9.js → command-handlers-Db8pu_ds.js} +484 -95
  10. package/dist/{compact-D_uvHurS.js → compact-LspQaGfl.js} +213 -199
  11. package/dist/{conversation-binding-data-BHRpYbX3.js → conversation-binding-data-CW2GuupV.js} +6 -10
  12. package/dist/doctor-contract-api.js +40 -43
  13. package/dist/{dynamic-tools-D2gYkSBO.js → dynamic-tools-BVdyWkUD.js} +2597 -1221
  14. package/dist/{effective-mcp-catalog-Bbd98Inw.js → effective-mcp-catalog-DIIhAtOZ.js} +20 -15
  15. package/dist/{event-projector-assistant-message-DTgnPPVF.js → event-projector-assistant-message-BwxV4d6N.js} +11 -23
  16. package/dist/{attempt-notifications-AP-a-Q5k.js → event-projector-usage-e5huIQWu.js} +113 -29
  17. package/dist/harness.js +80 -14
  18. package/dist/image-payload-sanitizer-CnfoUPCE.js +65 -0
  19. package/dist/index.js +591 -240
  20. package/dist/isolated-completion-C58NbL40.js +75 -0
  21. package/dist/local-runtime-attribution-B_2mnqhb.js +19 -0
  22. package/dist/media-understanding-provider.js +5 -5
  23. package/dist/{models-BBMKU4jA.js → models-BFlpn1Ex.js} +12 -17
  24. package/dist/native-subagent-monitor-DyTGmTUx.js +3111 -0
  25. package/dist/{plugin-app-cache-key-DztPafOA.js → plugin-app-cache-key-C3H2CVp6.js} +5 -5
  26. package/dist/plugin-marketplace-discovery-CzrGJdCR.js +385 -0
  27. package/dist/{computer-use-CYEc8U3w.js → plugin-metadata-cache-CNJPj4_b.js} +105 -5
  28. package/dist/protocol-validators-B4F7L8Rf.js +2346 -0
  29. package/dist/{rate-limits-CU-gVCCS.js → rate-limits-DZXmOL8-.js} +17 -35
  30. package/dist/{request-qKh331yu.js → request-BRl5_UfX.js} +3 -3
  31. package/dist/{run-attempt-CY9WlDLc.js → run-attempt-P8BdxC1I.js} +4837 -9047
  32. package/dist/{runtime-artifact-CmtG_t1L.js → runtime-artifact-BVSPdd6g.js} +8 -23
  33. package/dist/{sandbox-guard-NuhpXmPB.js → sandbox-guard-Cmwc9eIk.js} +6 -13
  34. package/dist/{session-binding-BXpo9ymA.js → session-binding-DPxIvMgE.js} +246 -265
  35. package/dist/{session-catalog-DyOgR4WH.js → session-catalog-xwNi_SDM.js} +159 -109
  36. package/dist/{session-cli-2PZaFLRh.js → session-cli-7hgXZDV7.js} +1 -1
  37. package/dist/session-retirement-DoMMJFKm.js +36 -0
  38. package/dist/{session-upstream-marker-B6U7tLw5.js → session-upstream-marker-CrIFX06J.js} +1 -1
  39. package/dist/{settled-turn-finalizer-CXY2Z9dT.js → settled-turn-finalizer-BRWBO4MO.js} +21 -13
  40. package/dist/{shared-client-DTbvH-XV.js → shared-client-kIBbGjk4.js} +587 -223
  41. package/dist/{side-question-_ulBk1AL.js → side-question-9HAL26l9.js} +101 -52
  42. package/dist/{thread-lifecycle-DF53ogiA.js → thread-lifecycle-Bu1BTDXr.js} +694 -753
  43. package/dist/thread-ownership-6kOQHlyC.js +92 -0
  44. package/dist/thread-resume-CPrJNQVM.js +30 -0
  45. package/dist/transcript-mirror-B9gGgnJS.js +570 -0
  46. package/dist/{transport-stdio-DXgGZ7Ib.js → transport-stdio-DIImwDVS.js} +11 -0
  47. package/dist/upstream-prompt-provenance-ClRBije0.js +41 -0
  48. package/dist/{upstream-session-fork-Dy4Xvuoj.js → upstream-session-fork-CE-_LdnP.js} +5 -5
  49. package/dist/{usage-CQ6GhD5-.js → usage-CHlV0e-o.js} +3 -3
  50. package/dist/{web-search-provider.runtime-CCFhg2z5.js → web-search-provider.runtime-DRw2Vl42.js} +8 -9
  51. package/openclaw.plugin.json +18 -3
  52. package/package.json +5 -5
  53. package/dist/conversation-control-xsIeXbo4.js +0 -287
  54. package/dist/event-projector-usage-Xp4AlR2k.js +0 -86
  55. package/dist/transcript-mirror-DzsdNCf5.js +0 -6924
@@ -1,23 +1,32 @@
1
- import { W as isJsonObject, a as getLeasedSharedCodexAppServerClient, l as releaseLeasedSharedCodexAppServerClient, v as CodexAppServerRpcError } from "./shared-client-DTbvH-XV.js";
2
- import { d as sessionBindingIdentity, t as CODEX_APP_SERVER_BINDING_GUARDED_REQUEST_TIMEOUT_MS } from "./session-binding-BXpo9ymA.js";
3
- import { _ as readCodexNotificationItem } from "./attempt-notifications-AP-a-Q5k.js";
4
- import { n as resolveCodexNativeExecutionBlock } from "./sandbox-guard-NuhpXmPB.js";
5
- import { a as resolveCodexBindingAppServerConnection, n as readCodexNotificationThreadId, r as readCodexNotificationTurnId } from "./notification-correlation-CU6_AnOb.js";
1
+ import { d as sessionBindingIdentity, t as CODEX_APP_SERVER_BINDING_GUARDED_REQUEST_TIMEOUT_MS } from "./session-binding-DPxIvMgE.js";
2
+ import { V as consumeCodexAppServerLiveThread, a as getLeasedSharedCodexAppServerClient, ct as isJsonObject, l as releaseLeasedSharedCodexAppServerClient, q as retainCodexAppServerLiveThread, z as CodexAppServerRpcError } from "./shared-client-kIBbGjk4.js";
3
+ import { _ as readCodexNotificationTurnId, a as closeCodexStartupClientBestEffort, d as unsubscribeCodexThreadBestEffort, g as readCodexNotificationThreadId, r as CodexAppServerUnsafeSubscriptionError, s as isCodexAlreadyTerminalInterruptError } from "./attempt-client-cleanup-DMLXUfa8.js";
4
+ import { i as readCodexThreadContextSnapshot, x as readCodexNotificationItem } from "./event-projector-usage-e5huIQWu.js";
5
+ import { t as isIncognitoSessionKey } from "./incognito-session-ET_ufDH-.js";
6
+ import { t as resumeCodexAppServerThread } from "./thread-resume-CPrJNQVM.js";
7
+ import { n as resolveCodexNativeExecutionBlock } from "./sandbox-guard-Cmwc9eIk.js";
8
+ import { t as isSameCodexAppServerThreadOwner } from "./thread-ownership-6kOQHlyC.js";
9
+ import { n as resolveCodexBindingAppServerConnection } from "./binding-connection-I5Bo-AsX.js";
10
+ import { asOptionalRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
11
+ import { createDeferred } from "openclaw/plugin-sdk/extension-shared";
6
12
  import { embeddedAgentLog, resolveCompactionTimeoutMs } from "openclaw/plugin-sdk/agent-harness-runtime";
7
13
  import { resolveAgentDir as resolveAgentDir$1, resolveDefaultAgentId } from "openclaw/plugin-sdk/agent-runtime";
14
+ import { AsyncLocalStorage } from "node:async_hooks";
15
+ import { coerceErrorMessage } from "openclaw/plugin-sdk/error-runtime";
16
+ import { KeyedAsyncQueue } from "openclaw/plugin-sdk/keyed-async-queue";
17
+ import { createDedupeCache } from "openclaw/plugin-sdk/dedupe-runtime";
8
18
  //#region extensions/codex/src/app-server/compact.ts
9
19
  /**
10
20
  * Native Codex app-server compaction bridge for bound OpenClaw sessions.
11
21
  */
12
- const warnedIgnoredCompactionOverrides = /* @__PURE__ */ new Set();
13
- const codexNativeCompactionQueues = /* @__PURE__ */ new Map();
22
+ const warnedIgnoredCompactionOverrides = createDedupeCache({
23
+ ttlMs: 0,
24
+ maxSize: 4096
25
+ });
26
+ const codexNativeCompactionQueue = new KeyedAsyncQueue();
14
27
  const CODEX_NATIVE_COMPACTION_INTERRUPT_GRACE_MS = 3e4;
15
- const CODEX_NO_ACTIVE_TURN_ERROR_CODE = -32600;
16
- const CODEX_NO_ACTIVE_TURN_ERROR_MESSAGE = "no active turn to interrupt";
17
- function isAlreadyTerminalInterruptError(error) {
18
- return error instanceof CodexAppServerRpcError && error.code === CODEX_NO_ACTIVE_TURN_ERROR_CODE && error.message === CODEX_NO_ACTIVE_TURN_ERROR_MESSAGE;
19
- }
20
28
  function watchCodexNativeCompactionCompletion(params) {
29
+ const runOutsideBindingLease = AsyncLocalStorage.snapshot();
21
30
  let settled = false;
22
31
  let requestStarted = false;
23
32
  let abortRequested = false;
@@ -26,10 +35,8 @@ function watchCodexNativeCompactionCompletion(params) {
26
35
  let compactionTurnId;
27
36
  let compactionItemId;
28
37
  let compactionItemCompleted = false;
29
- let resolveCompletion = (_result) => {};
30
- const completion = new Promise((resolve) => {
31
- resolveCompletion = resolve;
32
- });
38
+ let tokensAfter;
39
+ const { promise: completion, resolve: resolveCompletion } = createDeferred();
33
40
  let removeNotificationHandler = () => {};
34
41
  let removeCloseHandler = () => {};
35
42
  let removeAbortHandler = () => {};
@@ -45,17 +52,22 @@ function watchCodexNativeCompactionCompletion(params) {
45
52
  clearTimeout(interruptGraceTimeout);
46
53
  resolveCompletion(result);
47
54
  };
55
+ const complete = () => finish({
56
+ completed: true,
57
+ ...tokensAfter !== void 0 ? { tokensAfter } : {}
58
+ });
59
+ const fail = (reason) => finish({
60
+ completed: false,
61
+ reason
62
+ });
48
63
  const retireUnconfirmed = (reason) => {
49
64
  if (settled || retirementStarted) return;
50
65
  retirementStarted = true;
51
- params.retireUnconfirmed().then(() => finish({
52
- completed: false,
53
- reason
54
- })).catch((error) => {
66
+ runOutsideBindingLease(() => params.retireUnconfirmed()).then(() => fail(reason)).catch((error) => {
55
67
  embeddedAgentLog.error("failed to retire unconfirmed codex app-server compaction", {
56
68
  threadId: params.threadId,
57
69
  turnId: compactionTurnId,
58
- reason: formatCompactionError(error)
70
+ reason: coerceErrorMessage(error)
59
71
  });
60
72
  });
61
73
  };
@@ -65,23 +77,19 @@ function watchCodexNativeCompactionCompletion(params) {
65
77
  params.client.request("turn/interrupt", {
66
78
  threadId: params.threadId,
67
79
  turnId: compactionTurnId
68
- }, { timeoutMs: Math.max(1, params.interruptGraceMs) }).then(() => {
69
- finish({
70
- completed: false,
71
- reason: "codex app-server confirmed native compaction interruption"
72
- });
73
- }).catch((error) => {
74
- if (isAlreadyTerminalInterruptError(error)) {
75
- finish(compactionItemCompleted ? { completed: true } : {
76
- completed: false,
77
- reason: "codex app-server compaction reached terminal state without a completed compaction item"
78
- });
80
+ }, { timeoutMs: Math.max(1, params.interruptGraceMs) }).catch((error) => {
81
+ if (isCodexAlreadyTerminalInterruptError(error)) {
82
+ if (compactionItemCompleted) {
83
+ complete();
84
+ return;
85
+ }
86
+ fail("codex app-server compaction reached terminal state without a completed compaction item");
79
87
  return;
80
88
  }
81
89
  embeddedAgentLog.warn("codex app-server compaction interrupt request failed", {
82
90
  threadId: params.threadId,
83
91
  turnId: compactionTurnId,
84
- reason: formatCompactionError(error)
92
+ reason: coerceErrorMessage(error)
85
93
  });
86
94
  });
87
95
  };
@@ -121,6 +129,10 @@ function watchCodexNativeCompactionCompletion(params) {
121
129
  return;
122
130
  }
123
131
  if (compactionTurnId && notificationTurnId !== compactionTurnId) return;
132
+ if (notification.method === "thread/tokenUsage/updated") {
133
+ tokensAfter = readCodexThreadContextSnapshot(notification.params).activeContextTokens ?? tokensAfter;
134
+ return;
135
+ }
124
136
  const item = readCodexNotificationItem(notification.params);
125
137
  if (item?.type === "contextCompaction") {
126
138
  if (notification.method === "item/started") {
@@ -138,27 +150,15 @@ function watchCodexNativeCompactionCompletion(params) {
138
150
  const turn = isJsonObject(notification.params.turn) ? notification.params.turn : void 0;
139
151
  const status = typeof turn?.status === "string" ? turn.status : void 0;
140
152
  if (status !== "completed") {
141
- finish({
142
- completed: false,
143
- reason: `codex app-server compaction turn ended with status ${status ?? "unknown"}`
144
- });
153
+ fail(`codex app-server compaction turn ended with status ${status ?? "unknown"}`);
145
154
  return;
146
155
  }
147
- if (!compactionItemId) {
148
- finish({
149
- completed: false,
150
- reason: "codex app-server compaction turn completed without a compaction item"
151
- });
156
+ const incompleteReason = !compactionItemId ? "codex app-server compaction turn completed without a compaction item" : !compactionItemCompleted ? "codex app-server compaction turn completed before its compaction item" : void 0;
157
+ if (incompleteReason) {
158
+ fail(incompleteReason);
152
159
  return;
153
160
  }
154
- if (!compactionItemCompleted) {
155
- finish({
156
- completed: false,
157
- reason: "codex app-server compaction turn completed before its compaction item"
158
- });
159
- return;
160
- }
161
- finish({ completed: true });
161
+ complete();
162
162
  });
163
163
  removeCloseHandler = params.client.addCloseHandler(() => {
164
164
  retireUnconfirmed("codex app-server closed before native compaction completed");
@@ -179,57 +179,35 @@ function watchCodexNativeCompactionCompletion(params) {
179
179
  beginCompletionTimeout();
180
180
  if (abortRequested) beginInterruptGrace();
181
181
  },
182
- confirmRequestRejected: () => finish({
183
- completed: false,
184
- reason: "codex app-server rejected the compaction request"
185
- }),
182
+ confirmRequestRejected: () => fail("codex app-server rejected the compaction request"),
186
183
  retireUnconfirmedRequest: async (reason) => {
187
184
  retireUnconfirmed(reason);
188
185
  return await completion;
189
186
  },
190
187
  cancel: () => {
191
- if (!requestStarted) finish({
192
- completed: false,
193
- reason: "compaction request did not start"
194
- });
188
+ if (!requestStarted) fail("compaction request did not start");
195
189
  }
196
190
  };
197
191
  }
198
192
  async function runExclusiveCodexNativeCompaction(threadId, signal, run) {
199
- const previous = codexNativeCompactionQueues.get(threadId) ?? Promise.resolve();
200
- let releaseCurrent;
201
- const current = new Promise((resolve) => {
202
- releaseCurrent = resolve;
203
- });
204
- const queued = previous.then(() => current, () => current);
205
- codexNativeCompactionQueues.set(threadId, queued);
206
- try {
207
- await waitForCodexNativeCompactionQueue(previous, signal);
193
+ signal?.throwIfAborted();
194
+ let started = false;
195
+ const queued = codexNativeCompactionQueue.enqueue(threadId, async () => {
196
+ started = true;
208
197
  signal?.throwIfAborted();
209
- return await run();
210
- } finally {
211
- releaseCurrent();
212
- queued.then(() => {
213
- if (codexNativeCompactionQueues.get(threadId) === queued) codexNativeCompactionQueues.delete(threadId);
214
- });
215
- }
216
- }
217
- async function waitForCodexNativeCompactionQueue(previous, signal) {
218
- if (!signal) {
219
- await previous.catch(() => void 0);
220
- return;
221
- }
222
- signal.throwIfAborted();
198
+ return run();
199
+ });
200
+ if (!signal) return queued;
223
201
  let removeAbortListener = () => {};
224
202
  const aborted = new Promise((_, reject) => {
225
203
  const onAbort = () => {
226
- reject(signal.reason instanceof Error ? signal.reason : /* @__PURE__ */ new Error("compaction aborted"));
204
+ if (!started) reject(signal.reason instanceof Error ? signal.reason : /* @__PURE__ */ new Error("compaction aborted"));
227
205
  };
228
206
  removeAbortListener = () => signal.removeEventListener("abort", onAbort);
229
207
  signal.addEventListener("abort", onAbort, { once: true });
230
208
  });
231
209
  try {
232
- await Promise.race([previous.catch(() => void 0), aborted]);
210
+ return await Promise.race([queued, aborted]);
233
211
  } finally {
234
212
  removeAbortListener();
235
213
  }
@@ -246,8 +224,7 @@ function warnIfIgnoringOpenClawCompactionOverrides(params) {
246
224
  const ignoredConfig = readIgnoredCompactionOverridePaths(params);
247
225
  if (ignoredConfig.length === 0) return;
248
226
  const warningKey = ignoredConfig.join("\0");
249
- if (warnedIgnoredCompactionOverrides.has(warningKey)) return;
250
- warnedIgnoredCompactionOverrides.add(warningKey);
227
+ if (warnedIgnoredCompactionOverrides.check(warningKey)) return;
251
228
  embeddedAgentLog.warn("ignoring OpenClaw compaction overrides for Codex app-server compaction; Codex uses native server-side compaction", {
252
229
  sessionId: params.sessionId,
253
230
  sessionKey: params.sessionKey,
@@ -268,18 +245,16 @@ function readIgnoredCompactionOverridePaths(params) {
268
245
  }
269
246
  function readCompactionOverrideEntries(params) {
270
247
  const entries = [];
271
- const defaultCompaction = readRecord(readRecord(params.config?.agents)?.defaults)?.compaction;
272
- const defaultRecord = readRecord(defaultCompaction);
248
+ const defaultRecord = asOptionalRecord(params.config?.agents?.defaults?.compaction);
273
249
  if (defaultRecord) entries.push({
274
250
  path: "agents.defaults",
275
251
  record: defaultRecord
276
252
  });
277
253
  const agentId = readAgentIdFromSessionKey(params.sessionKey ?? params.sandboxSessionKey);
278
254
  if (!agentId) return entries;
279
- const agentCompaction = readRecord((Array.isArray(params.config?.agents?.list) ? params.config.agents.list : []).find((agent) => {
255
+ const agentRecord = asOptionalRecord((Array.isArray(params.config?.agents?.list) ? params.config.agents.list : []).find((agent) => {
280
256
  return (typeof agent?.id === "string" ? agent.id.trim().toLowerCase() : "") === agentId;
281
- }))?.compaction;
282
- const agentRecord = readRecord(agentCompaction);
257
+ })?.compaction);
283
258
  if (agentRecord) entries.push({
284
259
  path: `agents.list.${agentId}`,
285
260
  record: agentRecord,
@@ -293,9 +268,6 @@ function readAgentIdFromSessionKey(sessionKey) {
293
268
  if (parts.length < 3 || parts[0] !== "agent") return;
294
269
  return parts[1]?.trim() || void 0;
295
270
  }
296
- function readRecord(value) {
297
- return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
298
- }
299
271
  async function compactCodexNativeThread(params, options) {
300
272
  if (params.trigger !== "manual" && !options.allowNonManualNativeRequest) {
301
273
  embeddedAgentLog.info("skipping codex app-server compaction for non-manual trigger", {
@@ -303,27 +275,23 @@ async function compactCodexNativeThread(params, options) {
303
275
  sessionKey: params.sessionKey,
304
276
  trigger: params.trigger
305
277
  });
306
- return {
307
- ok: true,
278
+ return codexNativeCompactionResult(params, {
308
279
  compacted: false,
309
280
  reason: "codex app-server owns automatic compaction",
310
- result: {
311
- summary: "",
312
- firstKeptEntryId: "",
313
- tokensBefore: params.currentTokenCount ?? 0,
314
- details: {
315
- backend: "codex-app-server",
316
- skipped: true,
317
- reason: "non_manual_trigger",
318
- trigger: params.trigger ?? "unknown"
319
- }
281
+ details: {
282
+ backend: "codex-app-server",
283
+ skipped: true,
284
+ reason: "non_manual_trigger",
285
+ trigger: params.trigger ?? "unknown"
320
286
  }
321
- };
287
+ });
322
288
  }
289
+ const sandbox = params.sandbox;
323
290
  const nativeExecutionBlock = resolveCodexNativeExecutionBlock({
324
291
  config: params.config,
325
292
  sessionKey: params.sandboxSessionKey ?? params.sessionKey,
326
293
  sessionId: params.sessionId,
294
+ sandbox,
327
295
  surface: "native compaction"
328
296
  });
329
297
  if (nativeExecutionBlock) return {
@@ -342,6 +310,16 @@ async function compactCodexNativeThread(params, options) {
342
310
  reason: "no codex app-server thread binding",
343
311
  recovery: "missing_thread_binding"
344
312
  });
313
+ if (params.nativeToolSurface === "host-isolated" || initialBinding.nativeToolPolicyRestricted === true || initialBinding.ringZeroConfigFingerprint !== void 0) return codexNativeCompactionResult(params, {
314
+ compacted: false,
315
+ reason: "native compaction is unavailable for a host-isolated Codex session",
316
+ details: {
317
+ backend: "codex-app-server",
318
+ skipped: true,
319
+ reason: "native_tool_policy_restricted",
320
+ expectedThreadId: initialBinding.threadId
321
+ }
322
+ });
345
323
  let binding = initialBinding;
346
324
  const requestedAuthProfileId = params.authProfileId?.trim() || void 0;
347
325
  let connection;
@@ -359,7 +337,7 @@ async function compactCodexNativeThread(params, options) {
359
337
  return {
360
338
  ok: false,
361
339
  compacted: false,
362
- reason: formatCompactionError(error)
340
+ reason: coerceErrorMessage(error)
363
341
  };
364
342
  }
365
343
  const { appServer, usesSupervisionConnection } = connection;
@@ -389,6 +367,19 @@ async function compactCodexNativeThread(params, options) {
389
367
  agentDir: params.agentDir,
390
368
  config: params.config
391
369
  });
370
+ let releaseThreadSubscription;
371
+ let retainedThreadOwnership;
372
+ let compactionSucceeded = false;
373
+ let compactionRequestDefinitelyRejected = false;
374
+ let tokensAfter;
375
+ const releaseCompactionThread = async (threadId) => {
376
+ if (await unsubscribeCodexThreadBestEffort(client, {
377
+ threadId,
378
+ timeoutMs: 5e3
379
+ })) return;
380
+ await closeCodexStartupClientBestEffort(client);
381
+ throw new CodexAppServerUnsafeSubscriptionError(`Codex compaction thread subscription could not be released: ${threadId}`);
382
+ };
392
383
  const completionWatch = watchCodexNativeCompactionCompletion({
393
384
  client,
394
385
  threadId: binding.threadId,
@@ -396,6 +387,7 @@ async function compactCodexNativeThread(params, options) {
396
387
  timeoutMs: options.nativeCompletionTimeoutMs ?? resolveCompactionTimeoutMs(params.config),
397
388
  interruptGraceMs: options.nativeInterruptGraceMs ?? CODEX_NATIVE_COMPACTION_INTERRUPT_GRACE_MS,
398
389
  retireUnconfirmed: async () => {
390
+ releaseThreadSubscription = void 0;
399
391
  const transportStopped = await client.closeAndWait({
400
392
  exitTimeoutMs: 5e3,
401
393
  forceKillDelayMs: 250
@@ -413,21 +405,28 @@ async function compactCodexNativeThread(params, options) {
413
405
  throw new Error("failed to detach unconfirmed codex app-server thread binding");
414
406
  }
415
407
  });
416
- const beginNativeCompactionRequest = async (timeoutMs) => {
417
- completionWatch.beginRequest();
418
- const requestParams = { threadId: binding.threadId };
419
- if (timeoutMs === void 0) await client.request("thread/compact/start", requestParams);
420
- else await client.request("thread/compact/start", requestParams, { timeoutMs });
421
- };
422
- const settleNativeCompactionRequestError = async (error) => {
423
- if (error instanceof CodexAppServerRpcError) completionWatch.confirmRequestRejected();
424
- else await completionWatch.retireUnconfirmedRequest(`codex app-server compaction start was unconfirmed: ${formatCompactionError(error)}`);
408
+ const acquireThreadSubscription = async (timeoutMs) => {
409
+ if (!isIncognitoSessionKey(params.sessionKey)) {
410
+ retainedThreadOwnership = await consumeCodexAppServerLiveThread(client, binding.threadId);
411
+ if (!retainedThreadOwnership) await resumeCodexAppServerThread({
412
+ client,
413
+ abandonClient: async () => closeCodexStartupClientBestEffort(client),
414
+ request: {
415
+ threadId: binding.threadId,
416
+ excludeTurns: true
417
+ },
418
+ timeoutMs: timeoutMs ?? appServer.requestTimeoutMs,
419
+ ...params.abortSignal ? { signal: params.abortSignal } : {}
420
+ });
421
+ releaseThreadSubscription = async () => releaseCompactionThread(binding.threadId);
422
+ }
425
423
  };
426
424
  try {
427
- if (options.allowNonManualNativeRequest) {
428
- const guardedResult = await options.bindingStore.withLease(bindingIdentity, async () => {
429
- const currentBinding = await options.bindingStore.read(bindingIdentity);
430
- if (params.abortSignal?.aborted) return {
425
+ const guardedResult = await options.bindingStore.withLease(bindingIdentity, async () => {
426
+ const currentBinding = await options.bindingStore.read(bindingIdentity);
427
+ if (params.abortSignal?.aborted) {
428
+ if (!options.allowNonManualNativeRequest) params.abortSignal.throwIfAborted();
429
+ return {
431
430
  started: false,
432
431
  result: skippedCodexNativeCompactionResult(params, {
433
432
  reason: "codex app-server compaction aborted before native compaction",
@@ -436,61 +435,65 @@ async function compactCodexNativeThread(params, options) {
436
435
  currentThreadId: currentBinding?.threadId
437
436
  })
438
437
  };
439
- if (!currentBinding || !isSameNativeCompactionBinding(currentBinding, binding)) {
440
- embeddedAgentLog.warn("skipping codex app-server compaction because the thread binding changed", {
441
- sessionId: params.sessionId,
442
- sessionKey: params.sessionKey,
443
- expectedThreadId: binding.threadId,
444
- currentThreadId: currentBinding?.threadId
445
- });
446
- return {
447
- started: false,
448
- result: skippedCodexNativeCompactionResult(params, {
449
- reason: "codex app-server binding changed before native compaction",
450
- code: "binding_changed_before_native_compaction",
451
- expectedThreadId: binding.threadId,
452
- currentThreadId: currentBinding?.threadId
453
- })
454
- };
455
- }
456
- binding = currentBinding;
457
- await clearContextEngineProjectionBeforeNativeCompaction({
438
+ }
439
+ if (!currentBinding || !isSameNativeCompactionBinding(currentBinding, binding)) {
440
+ embeddedAgentLog.warn("skipping codex app-server compaction because the thread binding changed", {
458
441
  sessionId: params.sessionId,
459
- bindingStore: options.bindingStore,
460
- identity: bindingIdentity,
461
- binding
442
+ sessionKey: params.sessionKey,
443
+ expectedThreadId: binding.threadId,
444
+ currentThreadId: currentBinding?.threadId
462
445
  });
463
- try {
464
- await beginNativeCompactionRequest(Math.min(appServer.requestTimeoutMs, CODEX_APP_SERVER_BINDING_GUARDED_REQUEST_TIMEOUT_MS));
465
- return {
466
- started: true,
467
- accepted: true
468
- };
469
- } catch (error) {
446
+ return {
447
+ started: false,
448
+ result: options.allowNonManualNativeRequest ? skippedCodexNativeCompactionResult(params, {
449
+ reason: "codex app-server binding changed before native compaction",
450
+ code: "binding_changed_before_native_compaction",
451
+ expectedThreadId: binding.threadId,
452
+ currentThreadId: currentBinding?.threadId
453
+ }) : failedCodexThreadBindingCompactionResult(params, {
454
+ threadId: binding.threadId,
455
+ reason: "codex app-server binding changed before native compaction",
456
+ recovery: "stale_thread_binding"
457
+ })
458
+ };
459
+ }
460
+ binding = currentBinding;
461
+ const guardedRequestTimeoutMs = options.allowNonManualNativeRequest ? Math.min(appServer.requestTimeoutMs, CODEX_APP_SERVER_BINDING_GUARDED_REQUEST_TIMEOUT_MS) : void 0;
462
+ await acquireThreadSubscription(guardedRequestTimeoutMs);
463
+ await clearContextEngineProjectionBeforeNativeCompaction({
464
+ sessionId: params.sessionId,
465
+ bindingStore: options.bindingStore,
466
+ identity: bindingIdentity,
467
+ binding
468
+ });
469
+ try {
470
+ completionWatch.beginRequest();
471
+ if (guardedRequestTimeoutMs === void 0) await client.request("thread/compact/start", { threadId: binding.threadId });
472
+ else await client.request("thread/compact/start", { threadId: binding.threadId }, { timeoutMs: guardedRequestTimeoutMs });
473
+ return {
474
+ started: true,
475
+ accepted: true
476
+ };
477
+ } catch (error) {
478
+ if (error instanceof CodexAppServerRpcError) {
470
479
  await options.bindingStore.mutate(bindingIdentity, {
471
480
  kind: "set",
472
481
  binding
473
482
  });
474
- return {
475
- started: true,
476
- accepted: false,
477
- error
478
- };
483
+ compactionRequestDefinitelyRejected = !isCodexThreadNotFoundError(error);
479
484
  }
480
- });
481
- if (!guardedResult.started) return guardedResult.result;
482
- if (!guardedResult.accepted) {
483
- await settleNativeCompactionRequestError(guardedResult.error);
484
- throw guardedResult.error;
485
- }
486
- } else {
487
- params.abortSignal?.throwIfAborted();
488
- try {
489
- await beginNativeCompactionRequest();
490
- } catch (error) {
491
- await settleNativeCompactionRequestError(error);
492
- throw error;
485
+ return {
486
+ started: true,
487
+ accepted: false,
488
+ error
489
+ };
493
490
  }
491
+ });
492
+ if (!guardedResult.started) return guardedResult.result;
493
+ if (!guardedResult.accepted) {
494
+ if (guardedResult.error instanceof CodexAppServerRpcError) completionWatch.confirmRequestRejected();
495
+ else await completionWatch.retireUnconfirmedRequest(`codex app-server compaction start was unconfirmed: ${coerceErrorMessage(guardedResult.error)}`);
496
+ throw guardedResult.error;
494
497
  }
495
498
  embeddedAgentLog.info("started codex app-server compaction", {
496
499
  sessionId: params.sessionId,
@@ -498,32 +501,44 @@ async function compactCodexNativeThread(params, options) {
498
501
  });
499
502
  const completion = await completionWatch.completion;
500
503
  if (!completion.completed) throw new Error(completion.reason);
504
+ tokensAfter = completion.tokensAfter;
501
505
  embeddedAgentLog.info("completed codex app-server compaction", {
502
506
  sessionId: params.sessionId,
503
507
  threadId: binding.threadId
504
508
  });
509
+ compactionSucceeded = true;
505
510
  } catch (error) {
506
511
  if (isCodexThreadNotFoundError(error)) return failedCodexThreadBindingCompactionResult(params, {
507
512
  threadId: binding.threadId,
508
- reason: formatCompactionError(error),
513
+ reason: coerceErrorMessage(error),
509
514
  recovery: "stale_thread_binding"
510
515
  });
511
516
  embeddedAgentLog.warn("codex app-server compaction failed", {
512
517
  sessionId: params.sessionId,
513
518
  sessionKey: params.sessionKey,
514
519
  threadId: binding.threadId,
515
- reason: formatCompactionError(error)
520
+ reason: coerceErrorMessage(error)
516
521
  });
517
522
  return {
518
523
  ok: false,
519
524
  compacted: false,
520
- reason: formatCompactionError(error)
525
+ reason: coerceErrorMessage(error)
521
526
  };
522
527
  } finally {
523
528
  completionWatch.cancel();
524
- if (shouldReleaseDefaultLease) releaseLeasedSharedCodexAppServerClient(client);
529
+ try {
530
+ if ((compactionSucceeded || compactionRequestDefinitelyRejected) && retainedThreadOwnership) {
531
+ const ownership = retainedThreadOwnership;
532
+ if (!(isSameCodexAppServerThreadOwner(await options.bindingStore.read(bindingIdentity), binding) && await options.bindingStore.withLease(bindingIdentity, async () => {
533
+ if (!isSameCodexAppServerThreadOwner(await options.bindingStore.read(bindingIdentity), binding)) return false;
534
+ return await retainCodexAppServerLiveThread(client, binding.threadId, ownership.release, ownership.configFingerprint, ownership.serviceTier);
535
+ }))) await releaseThreadSubscription?.();
536
+ } else await releaseThreadSubscription?.();
537
+ } finally {
538
+ if (shouldReleaseDefaultLease) releaseLeasedSharedCodexAppServerClient(client);
539
+ }
525
540
  }
526
- const resultDetails = {
541
+ const details = {
527
542
  backend: "codex-app-server",
528
543
  threadId: binding.threadId,
529
544
  signal: "thread/compact/start",
@@ -534,16 +549,11 @@ async function compactCodexNativeThread(params, options) {
534
549
  trigger: params.trigger ?? "unknown"
535
550
  } : {}
536
551
  };
537
- return {
538
- ok: true,
552
+ return codexNativeCompactionResult(params, {
539
553
  compacted: true,
540
- result: {
541
- summary: "",
542
- firstKeptEntryId: "",
543
- tokensBefore: params.currentTokenCount ?? 0,
544
- details: resultDetails
545
- }
546
- };
554
+ tokensAfter,
555
+ details
556
+ });
547
557
  });
548
558
  } catch (error) {
549
559
  if (params.abortSignal?.aborted) {
@@ -562,27 +572,35 @@ async function compactCodexNativeThread(params, options) {
562
572
  throw error;
563
573
  }
564
574
  }
565
- function skippedCodexNativeCompactionResult(params, skipped) {
575
+ function codexNativeCompactionResult(params, outcome) {
566
576
  return {
567
577
  ok: true,
568
- compacted: false,
569
- reason: skipped.reason,
578
+ compacted: outcome.compacted,
579
+ ...outcome.reason ? { reason: outcome.reason } : {},
570
580
  result: {
571
581
  summary: "",
572
582
  firstKeptEntryId: "",
573
583
  tokensBefore: params.currentTokenCount ?? 0,
574
- details: {
575
- backend: "codex-app-server",
576
- skipped: true,
577
- reason: skipped.code,
578
- request: "after_context_engine",
579
- trigger: params.trigger ?? "unknown",
580
- ...skipped.expectedThreadId ? { expectedThreadId: skipped.expectedThreadId } : {},
581
- ...skipped.currentThreadId ? { currentThreadId: skipped.currentThreadId } : {}
582
- }
584
+ ...outcome.tokensAfter !== void 0 ? { tokensAfter: outcome.tokensAfter } : {},
585
+ details: outcome.details
583
586
  }
584
587
  };
585
588
  }
589
+ function skippedCodexNativeCompactionResult(params, skipped) {
590
+ return codexNativeCompactionResult(params, {
591
+ compacted: false,
592
+ reason: skipped.reason,
593
+ details: {
594
+ backend: "codex-app-server",
595
+ skipped: true,
596
+ reason: skipped.code,
597
+ request: "after_context_engine",
598
+ trigger: params.trigger ?? "unknown",
599
+ ...skipped.expectedThreadId ? { expectedThreadId: skipped.expectedThreadId } : {},
600
+ ...skipped.currentThreadId ? { currentThreadId: skipped.currentThreadId } : {}
601
+ }
602
+ });
603
+ }
586
604
  function failedCodexThreadBindingCompactionResult(params, recovery) {
587
605
  embeddedAgentLog.warn("codex app-server compaction could not use thread binding", {
588
606
  sessionId: params.sessionId,
@@ -620,14 +638,10 @@ async function clearContextEngineProjectionBeforeNativeCompaction(params) {
620
638
  });
621
639
  }
622
640
  function isSameNativeCompactionBinding(current, expected) {
623
- return current.threadId === expected.threadId && current.authProfileId === expected.authProfileId && current.contextEngine?.engineId === expected.contextEngine?.engineId && current.contextEngine?.policyFingerprint === expected.contextEngine?.policyFingerprint && current.contextEngine?.projection?.mode === expected.contextEngine?.projection?.mode && current.contextEngine?.projection?.epoch === expected.contextEngine?.projection?.epoch && current.contextEngine?.projection?.fingerprint === expected.contextEngine?.projection?.fingerprint;
641
+ return isSameCodexAppServerThreadOwner(current, expected) && current.authProfileId === expected.authProfileId && current.contextEngine?.engineId === expected.contextEngine?.engineId && current.contextEngine?.policyFingerprint === expected.contextEngine?.policyFingerprint && current.contextEngine?.projection?.mode === expected.contextEngine?.projection?.mode && current.contextEngine?.projection?.epoch === expected.contextEngine?.projection?.epoch && current.contextEngine?.projection?.fingerprint === expected.contextEngine?.projection?.fingerprint;
624
642
  }
625
643
  function isCodexThreadNotFoundError(error) {
626
- return formatCompactionError(error).toLowerCase().includes("thread not found");
627
- }
628
- function formatCompactionError(error) {
629
- if (error instanceof Error) return error.message;
630
- return String(error);
644
+ return coerceErrorMessage(error).toLowerCase().includes("thread not found");
631
645
  }
632
646
  //#endregion
633
647
  export { maybeCompactCodexAppServerSession };