|
28 | 28 | PRE_TOOL_TEXT = "I will inspect the root directory." |
29 | 29 | FINAL_TEXT = "The root contains README.md and proxy_server.py." |
30 | 30 | TOOL_CALL_ID = "call_root_ls" |
| 31 | +DEVELOPER_INSTRUCTIONS = "developer instructions" |
| 32 | +ENVIRONMENT_CONTEXT = "<environment_context><cwd>/repo</cwd></environment_context>" |
| 33 | + |
| 34 | + |
| 35 | +def record(role: str, text: str) -> dict[str, Any]: |
| 36 | + return proxy_server.transcript_record(role, text, [proxy_server.provider_message(role, text)]) |
| 37 | + |
| 38 | + |
| 39 | +def provider_texts(records: list[dict[str, Any]]) -> list[str]: |
| 40 | + return [str(record.get("text") or "") for record in records] |
31 | 41 |
|
32 | 42 |
|
33 | 43 | class MockCompactUpstream(BaseHTTPRequestHandler): |
@@ -341,6 +351,114 @@ def test_request_without_override_preserves_codex_body() -> None: |
341 | 351 | assert request_log[-1]["forwarded_body"] == original_body |
342 | 352 |
|
343 | 353 |
|
| 354 | +def test_clean_transcript_preserves_repeated_input_context_records() -> None: |
| 355 | + input_mapped_transcript = [ |
| 356 | + record("developer", DEVELOPER_INSTRUCTIONS), |
| 357 | + record("user", ENVIRONMENT_CONTEXT), |
| 358 | + record("user", "first real question"), |
| 359 | + record("assistant", "first answer"), |
| 360 | + record("developer", DEVELOPER_INSTRUCTIONS), |
| 361 | + record("user", ENVIRONMENT_CONTEXT), |
| 362 | + record("user", "second real question"), |
| 363 | + ] |
| 364 | + |
| 365 | + cleaned = proxy_server.clean_transcript(input_mapped_transcript) |
| 366 | + |
| 367 | + assert provider_texts(cleaned) == [ |
| 368 | + DEVELOPER_INSTRUCTIONS, |
| 369 | + ENVIRONMENT_CONTEXT, |
| 370 | + "first real question", |
| 371 | + "first answer", |
| 372 | + DEVELOPER_INSTRUCTIONS, |
| 373 | + ENVIRONMENT_CONTEXT, |
| 374 | + "second real question", |
| 375 | + ] |
| 376 | + |
| 377 | + |
| 378 | +def test_control_intercept_preserves_existing_transcript_when_input_is_prefix_only() -> None: |
| 379 | + with tempfile.TemporaryDirectory() as temp_dir: |
| 380 | + store = proxy_server.ProxyStore(Path(temp_dir) / "proxy_state.json") |
| 381 | + existing = proxy_server.clean_transcript( |
| 382 | + [ |
| 383 | + record("developer", DEVELOPER_INSTRUCTIONS), |
| 384 | + record("user", ENVIRONMENT_CONTEXT), |
| 385 | + record("user", "first real question"), |
| 386 | + record("assistant", "first answer"), |
| 387 | + ] |
| 388 | + ) |
| 389 | + store.sessions[SESSION_ID] = proxy_server.ProxySession( |
| 390 | + id=SESSION_ID, |
| 391 | + title="Codex fake", |
| 392 | + transcript=existing, |
| 393 | + status="mirror", |
| 394 | + ) |
| 395 | + body = { |
| 396 | + "input": [ |
| 397 | + proxy_server.provider_message("developer", DEVELOPER_INSTRUCTIONS), |
| 398 | + proxy_server.provider_message("user", ENVIRONMENT_CONTEXT), |
| 399 | + proxy_server.provider_message("user", "ctx"), |
| 400 | + ] |
| 401 | + } |
| 402 | + |
| 403 | + store.record_control_intercept(SESSION_ID, body, {"x-codex-session-id": SESSION_ID}, "ctx") |
| 404 | + |
| 405 | + session = store.get_session(SESSION_ID) |
| 406 | + assert session is not None |
| 407 | + assert provider_texts(session["transcript"]) == provider_texts(existing) |
| 408 | + assert store.sessions[SESSION_ID].request_log[-1]["kind"] == "context_control_intercept" |
| 409 | + |
| 410 | + |
| 411 | +def test_codex_local_session_sync_does_not_fallback_to_prefix_only_transcript() -> None: |
| 412 | + with tempfile.TemporaryDirectory() as temp_dir: |
| 413 | + previous_sessions_dir = web_server.CODEX_LOCAL_SESSIONS_DIR |
| 414 | + previous_proxy_state_file = web_server.PROXY_STATE_FILE |
| 415 | + try: |
| 416 | + temp_path = Path(temp_dir) |
| 417 | + web_server.CODEX_LOCAL_SESSIONS_DIR = temp_path / "missing-sessions" |
| 418 | + web_server.PROXY_STATE_FILE = temp_path / "proxy_state.json" |
| 419 | + web_server.PROXY_STATE_FILE.write_text( |
| 420 | + json.dumps( |
| 421 | + { |
| 422 | + "active_session_id": SESSION_ID, |
| 423 | + "sessions": [ |
| 424 | + { |
| 425 | + "id": SESSION_ID, |
| 426 | + "updated_at": "2026-05-09T00:00:00Z", |
| 427 | + "request_log": [ |
| 428 | + { |
| 429 | + "body": { |
| 430 | + "input": [ |
| 431 | + proxy_server.provider_message("developer", DEVELOPER_INSTRUCTIONS), |
| 432 | + proxy_server.provider_message("user", ENVIRONMENT_CONTEXT), |
| 433 | + ] |
| 434 | + } |
| 435 | + } |
| 436 | + ], |
| 437 | + } |
| 438 | + ], |
| 439 | + } |
| 440 | + ), |
| 441 | + encoding="utf-8", |
| 442 | + ) |
| 443 | + |
| 444 | + assert web_server.latest_proxy_instruction_prefix_records() |
| 445 | + assert web_server.codex_local_session_transcript(SESSION_ID) == [] |
| 446 | + finally: |
| 447 | + web_server.CODEX_LOCAL_SESSIONS_DIR = previous_sessions_dir |
| 448 | + web_server.PROXY_STATE_FILE = previous_proxy_state_file |
| 449 | + |
| 450 | + |
| 451 | +def test_prefix_only_codex_local_session_is_not_conversation() -> None: |
| 452 | + transcript = web_server.normalize_transcript( |
| 453 | + [ |
| 454 | + record("developer", DEVELOPER_INSTRUCTIONS), |
| 455 | + record("user", ENVIRONMENT_CONTEXT), |
| 456 | + ] |
| 457 | + ) |
| 458 | + |
| 459 | + assert not web_server.transcript_has_conversation_records(transcript) |
| 460 | + |
| 461 | + |
344 | 462 | def test_local_compact_without_override_preserves_codex_body() -> None: |
345 | 463 | with tempfile.TemporaryDirectory() as temp_dir: |
346 | 464 | store = proxy_server.ProxyStore(Path(temp_dir) / "proxy_state.json") |
|
0 commit comments