From 66dcc4ee8fd1cebc56736b400f138dac3f8a9adb Mon Sep 17 00:00:00 2001 From: Neerav Makwana <261249544+neeravmakwana@users.noreply.github.com> Date: Thu, 21 May 2026 11:01:35 -0400 Subject: [PATCH] fix(codex): beta blocker - keep context engine on canonical session key (#84954) Merged via squash. Prepared head SHA: 6cdccaa007d8f71ac042d264e3aea08f2203c810 Co-authored-by: neeravmakwana <261249544+neeravmakwana@users.noreply.github.com> Co-authored-by: jalehman <550978+jalehman@users.noreply.github.com> Reviewed-by: @jalehman --- CHANGELOG.md | 1 + .../run-attempt.context-engine.test.ts | 33 +++++++++++++++++++ .../codex/src/app-server/run-attempt.ts | 27 +++++++-------- 3 files changed, 48 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bd12c15c9a3..f20ed40e311 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,7 @@ Docs: https://docs.openclaw.ai - CLI/perf: keep `agents --help` out of agents action/runtime imports so help, completion, and command discovery paths avoid loading the full agents runtime. (#84483) Thanks @frankekn. - CLI/perf: keep `secrets --help` and `nodes --help` on the precomputed help path so parent help avoids loading action-heavy command runtime modules. (#84818) Thanks @frankekn. - CLI/perf: serve `doctor`, `gateway`, `models`, and `plugins` parent help from startup metadata so common subcommand help avoids full CLI program construction. (#84786) Thanks @frankekn. +- Codex/Lossless: keep context-engine history on the canonical run session when Telegram DMs use per-peer runtime policy keys. Fixes #84936. (#84954) Thanks @neeravmakwana. ## 2026.5.20 diff --git a/extensions/codex/src/app-server/run-attempt.context-engine.test.ts b/extensions/codex/src/app-server/run-attempt.context-engine.test.ts index 31ef5b1bedf..de67d0b79ff 100644 --- a/extensions/codex/src/app-server/run-attempt.context-engine.test.ts +++ b/extensions/codex/src/app-server/run-attempt.context-engine.test.ts @@ -348,6 +348,39 @@ describe("runCodexAppServerAttempt context-engine lifecycle", () => { expect(openSpy).not.toHaveBeenCalled(); }); + it("keeps context-engine history bound to the run session when sandbox key differs", async () => { + const sessionFile = path.join(tempDir, "session.jsonl"); + const workspaceDir = path.join(tempDir, "workspace"); + SessionManager.open(sessionFile).appendMessage( + assistantMessage("canonical main context", Date.now()) as never, + ); + const contextEngine = createContextEngine(); + const harness = createStartedThreadHarness(); + const params = createParams(sessionFile, workspaceDir); + params.sessionKey = "agent:main:main"; + params.sandboxSessionKey = "agent:main:telegram:default:direct:12345"; + params.contextEngine = contextEngine; + + const run = runCodexAppServerAttempt(params); + await harness.waitForMethod("turn/start"); + + if (!contextEngine.bootstrap) { + throw new Error("expected bootstrap hook"); + } + const bootstrapParams = requireFirstCallArg(contextEngine.bootstrap, "bootstrap") as Parameters< + NonNullable + >[0]; + expect(bootstrapParams.sessionKey).toBe("agent:main:main"); + + const assembleParams = requireFirstCallArg(contextEngine.assemble, "assemble") as Parameters< + ContextEngine["assemble"] + >[0]; + expect(assembleParams.sessionKey).toBe("agent:main:main"); + + await harness.completeTurn(); + await run; + }); + it("uses the runtime token budget for large Codex context-engine projections", async () => { const sessionFile = path.join(tempDir, "session.jsonl"); const workspaceDir = path.join(tempDir, "workspace"); diff --git a/extensions/codex/src/app-server/run-attempt.ts b/extensions/codex/src/app-server/run-attempt.ts index b3651b6d810..d1480d45f21 100644 --- a/extensions/codex/src/app-server/run-attempt.ts +++ b/extensions/codex/src/app-server/run-attempt.ts @@ -808,6 +808,7 @@ export async function runCodexAppServerAttempt( await fs.mkdir(resolvedWorkspace, { recursive: true }); const sandboxSessionKey = params.sandboxSessionKey?.trim() || params.sessionKey?.trim() || params.sessionId; + const contextSessionKey = params.sessionKey?.trim() || sandboxSessionKey; const sandbox = await resolveSandboxContext({ config: params.config, sessionKey: sandboxSessionKey, @@ -882,7 +883,7 @@ export async function runCodexAppServerAttempt( }); const runtimeParams = { ...params, - sessionKey: sandboxSessionKey, + sessionKey: contextSessionKey, ...(startupAuthProfileId ? { authProfileId: startupAuthProfileId } : {}), }; let activeSessionId = params.sessionId; @@ -1000,7 +1001,7 @@ export async function runCodexAppServerAttempt( hadSessionFile, contextEngine: activeContextEngine, sessionId: activeSessionId, - sessionKey: sandboxSessionKey, + sessionKey: contextSessionKey, sessionFile: activeSessionFile, runtimeContext: buildActiveContextEngineRuntimeContext(), runMaintenance: runHarnessContextEngineMaintenance, @@ -1014,7 +1015,7 @@ export async function runCodexAppServerAttempt( params, resolvedWorkspace, effectiveWorkspace, - sessionKey: sandboxSessionKey, + sessionKey: contextSessionKey, sessionAgentId, }); const baseDeveloperInstructions = joinPresentSections( @@ -1047,7 +1048,7 @@ export async function runCodexAppServerAttempt( const assembled = await assembleHarnessContextEngine({ contextEngine: activeContextEngine, sessionId: activeSessionId, - sessionKey: sandboxSessionKey, + sessionKey: contextSessionKey, messages: historyMessages, tokenBudget: params.contextTokenBudget, availableTools: new Set(toolBridge.specs.map((tool) => tool.name).filter(isNonEmptyString)), @@ -1087,7 +1088,7 @@ export async function runCodexAppServerAttempt( : { project: true, reason: "per-turn-projection" }; embeddedAgentLog.info("codex app-server context-engine projection decision", { sessionId: params.sessionId, - sessionKey: sandboxSessionKey, + sessionKey: contextSessionKey, engineId: activeContextEngine.info.id, mode: contextEngineProjection?.mode ?? assembled.contextProjection?.mode ?? "per_turn", epoch: contextEngineProjection?.epoch, @@ -1148,7 +1149,7 @@ export async function runCodexAppServerAttempt( }; const systemPromptReport = buildCodexSystemPromptReport({ attempt: params, - sessionKey: sandboxSessionKey, + sessionKey: contextSessionKey, workspaceDir: effectiveWorkspace, developerInstructions: promptBuild.developerInstructions, workspaceBootstrapContext, @@ -2254,7 +2255,7 @@ export async function runCodexAppServerAttempt( "codex app-server context-engine turn overflowed; forcing context-engine compaction", { sessionId: activeSessionId, - sessionKey: sandboxSessionKey, + sessionKey: contextSessionKey, threadId: thread.threadId, engineId: activeContextEngine.info.id, tokenBudget: params.contextTokenBudget, @@ -2273,7 +2274,7 @@ export async function runCodexAppServerAttempt( activeContextEngine, { sessionId: activeSessionId, - sessionKey: sandboxSessionKey, + sessionKey: contextSessionKey, sessionFile: activeSessionFile, tokenBudget: params.contextTokenBudget, force: true, @@ -2291,7 +2292,7 @@ export async function runCodexAppServerAttempt( ); embeddedAgentLog.info("codex app-server context-engine forced compaction result", { sessionId: activeSessionId, - sessionKey: sandboxSessionKey, + sessionKey: contextSessionKey, engineId: activeContextEngine.info.id, ok: compactResult.ok, compacted: compactResult.compacted, @@ -2307,7 +2308,7 @@ export async function runCodexAppServerAttempt( await runHarnessContextEngineMaintenance({ contextEngine: activeContextEngine, sessionId: activeSessionId, - sessionKey: sandboxSessionKey, + sessionKey: contextSessionKey, sessionFile: activeSessionFile, reason: "compaction", runtimeContext: maintenanceRuntimeContext, @@ -2317,7 +2318,7 @@ export async function runCodexAppServerAttempt( } catch (compactErr) { embeddedAgentLog.warn("codex app-server context-engine forced compaction failed", { sessionId: params.sessionId, - sessionKey: sandboxSessionKey, + sessionKey: contextSessionKey, engineId: activeContextEngine.info.id, error: formatErrorMessage(compactErr), }); @@ -2682,7 +2683,7 @@ export async function runCodexAppServerAttempt( params, agentId: sessionAgentId, result, - sessionKey: sandboxSessionKey, + sessionKey: contextSessionKey, threadId: thread.threadId, turnId: activeTurnId, }); @@ -2715,7 +2716,7 @@ export async function runCodexAppServerAttempt( aborted: finalAborted, yieldAborted: Boolean(result.yieldDetected), sessionIdUsed: activeSessionId, - sessionKey: sandboxSessionKey, + sessionKey: contextSessionKey, sessionFile: activeSessionFile, messagesSnapshot: finalMessages, prePromptMessageCount,