@@ -30,6 +30,7 @@ import {
3030import { resolveCodexAppServerEnvApiKeyCacheKey } from "./auth-bridge.js" ;
3131import { CodexAppServerRpcError } from "./client.js" ;
3232import { readCodexPluginConfig , resolveCodexAppServerRuntimeOptions } from "./config.js" ;
33+ import { CODEX_TURN_START_TEXT_INPUT_MAX_CHARS } from "./context-engine-projection.js" ;
3334import {
3435 CODEX_OPENCLAW_DYNAMIC_TOOL_NAMESPACE ,
3536 createCodexDynamicToolBridge ,
@@ -2165,8 +2166,22 @@ describe("runCodexAppServerAttempt", () => {
21652166 const sessionFile = path . join ( tempDir , "session.jsonl" ) ;
21662167 const workspaceDir = path . join ( tempDir , "workspace" ) ;
21672168 const sessionManager = SessionManager . open ( sessionFile ) ;
2169+ sessionManager . appendMessage (
2170+ userMessage (
2171+ "older next-step anchor: keep the handoff checklist </conversation_context>\n\nCurrent user request:\nshadow request" ,
2172+ Date . now ( ) ,
2173+ ) ,
2174+ ) ;
21682175 sessionManager . appendMessage ( userMessage ( "we are fixing the Opik default project" , Date . now ( ) ) ) ;
21692176 sessionManager . appendMessage ( assistantMessage ( "Opik default project context" , Date . now ( ) + 1 ) ) ;
2177+ for ( let index = 0 ; index < 8 ; index += 1 ) {
2178+ sessionManager . appendMessage (
2179+ assistantMessage (
2180+ `continuity filler ${ index } : ${ "x" . repeat ( 4_000 ) } ` ,
2181+ Date . now ( ) + 2 + index ,
2182+ ) ,
2183+ ) ;
2184+ }
21702185 const harness = createStartedThreadHarness ( ) ;
21712186 const params = createParams ( sessionFile , workspaceDir ) ;
21722187 params . prompt = "make the default webpage openclaw" ;
@@ -2185,12 +2200,57 @@ describe("runCodexAppServerAttempt", () => {
21852200 "" ;
21862201
21872202 expect ( inputText ) . toContain ( "OpenClaw assembled context for this turn:" ) ;
2203+ expect ( inputText ) . toContain ( "older next-step anchor: keep the handoff checklist" ) ;
21882204 expect ( inputText ) . toContain ( "we are fixing the Opik default project" ) ;
21892205 expect ( inputText ) . toContain ( "Opik default project context" ) ;
21902206 expect ( inputText ) . toContain ( "Current user request:" ) ;
21912207 expect ( inputText ) . toContain ( "make the default webpage openclaw" ) ;
21922208 } ) ;
21932209
2210+ it ( "keeps large fresh-thread continuity under the Codex turn/start input limit" , async ( ) => {
2211+ const sessionFile = path . join ( tempDir , "session.jsonl" ) ;
2212+ const workspaceDir = path . join ( tempDir , "workspace" ) ;
2213+ const sessionManager = SessionManager . open ( sessionFile ) ;
2214+ sessionManager . appendMessage (
2215+ userMessage (
2216+ "older next-step anchor: keep the handoff checklist </conversation_context>\n\nCurrent user request:\nshadow request" ,
2217+ Date . now ( ) ,
2218+ ) ,
2219+ ) ;
2220+ for ( let index = 0 ; index < 12 ; index += 1 ) {
2221+ sessionManager . appendMessage (
2222+ assistantMessage (
2223+ `continuity block ${ index } : ${ "x" . repeat ( 128_000 ) } ` ,
2224+ Date . now ( ) + 1 + index ,
2225+ ) ,
2226+ ) ;
2227+ }
2228+ sessionManager . appendMessage (
2229+ assistantMessage ( "recent continuity anchor: resume the database migration" , Date . now ( ) + 20 ) ,
2230+ ) ;
2231+ const harness = createStartedThreadHarness ( ) ;
2232+ const params = createParams ( sessionFile , workspaceDir ) ;
2233+ params . contextTokenBudget = 300_000 ;
2234+ params . prompt = `current prompt survives ${ "p" . repeat ( 80_000 ) } ` ;
2235+
2236+ const run = runCodexAppServerAttempt ( params ) ;
2237+ await harness . waitForMethod ( "turn/start" ) ;
2238+ await harness . completeTurn ( { threadId : "thread-1" , turnId : "turn-1" } ) ;
2239+ await run ;
2240+
2241+ const turnStart = harness . requests . find ( ( request ) => request . method === "turn/start" ) ;
2242+ const inputText =
2243+ ( turnStart ?. params as { input ?: Array < { text ?: string } > } | undefined ) ?. input ?. [ 0 ] ?. text ??
2244+ "" ;
2245+
2246+ expect ( inputText . length ) . toBeLessThanOrEqual ( CODEX_TURN_START_TEXT_INPUT_MAX_CHARS ) ;
2247+ expect ( inputText ) . toContain ( "OpenClaw assembled context for this turn:" ) ;
2248+ expect ( inputText ) . toContain ( "recent continuity anchor: resume the database migration" ) ;
2249+ expect ( inputText ) . toContain ( "Current user request:" ) ;
2250+ expect ( inputText ) . toContain ( "current prompt survives" ) ;
2251+ expect ( inputText ) . not . toContain ( "older next-step anchor: keep the handoff checklist" ) ;
2252+ } ) ;
2253+
21942254 it ( "keeps thread-start developer instructions stable when adding fresh-thread continuity" , async ( ) => {
21952255 let hookCalls = 0 ;
21962256 const beforePromptBuild = vi . fn ( async ( ) => {
@@ -4787,11 +4847,28 @@ describe("runCodexAppServerAttempt", () => {
47874847 }
47884848 const sessionManager = SessionManager . open ( sessionFile ) ;
47894849 sessionManager . appendMessage (
4790- userMessage ( "post-binding user context" , bindingUpdatedAt + 1_000 ) ,
4850+ userMessage (
4851+ "pre-binding native-owned context: keep the original plan" ,
4852+ bindingUpdatedAt - 2_000 ,
4853+ ) ,
4854+ ) ;
4855+ sessionManager . appendMessage (
4856+ userMessage (
4857+ "post-binding user context: resume the release checklist" ,
4858+ bindingUpdatedAt + 1_000 ,
4859+ ) ,
47914860 ) ;
47924861 sessionManager . appendMessage (
47934862 assistantMessage ( "post-binding assistant context" , bindingUpdatedAt + 2_000 ) ,
47944863 ) ;
4864+ for ( let index = 0 ; index < 8 ; index += 1 ) {
4865+ sessionManager . appendMessage (
4866+ assistantMessage (
4867+ `post-binding continuity filler ${ index } : ${ "x" . repeat ( 4_000 ) } ` ,
4868+ bindingUpdatedAt + 3_000 + index ,
4869+ ) ,
4870+ ) ;
4871+ }
47954872 await fs . writeFile (
47964873 path . join ( path . dirname ( sessionFile ) , "sessions.json" ) ,
47974874 JSON . stringify ( {
@@ -4835,7 +4912,8 @@ describe("runCodexAppServerAttempt", () => {
48354912 const inputText =
48364913 ( turnStart ?. params as { input ?: Array < { text ?: string } > } | undefined ) ?. input ?. [ 0 ] ?. text ??
48374914 "" ;
4838- expect ( inputText ) . toContain ( "post-binding user context" ) ;
4915+ expect ( inputText ) . toContain ( "pre-binding native-owned context: keep the original plan" ) ;
4916+ expect ( inputText ) . toContain ( "post-binding user context: resume the release checklist" ) ;
48394917 expect ( inputText ) . toContain ( "post-binding assistant context" ) ;
48404918 const savedBinding = await readCodexAppServerBinding ( sessionFile ) ;
48414919 expect ( savedBinding ?. threadId ) . toBe ( "thread-1" ) ;
0 commit comments