fix(codex): beta blocker - keep context engine on canonical session key (#84954)

Merged via squash.

Prepared head SHA: 6cdccaa007
Co-authored-by: neeravmakwana <261249544+neeravmakwana@users.noreply.github.com>
Co-authored-by: jalehman <550978+jalehman@users.noreply.github.com>
Reviewed-by: @jalehman
This commit is contained in:
Neerav Makwana
2026-05-21 11:01:35 -04:00
committed by GitHub
parent 1b1580cbc3
commit 66dcc4ee8f
3 changed files with 48 additions and 13 deletions

View File

@@ -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

View File

@@ -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<ContextEngine["bootstrap"]>
>[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");

View File

@@ -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,