codex-agent-framework 0.1.60__tar.gz → 0.1.62__tar.gz

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 (158) hide show
  1. {codex_agent_framework-0.1.60/codex_agent_framework.egg-info → codex_agent_framework-0.1.62}/PKG-INFO +1 -1
  2. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/context/__init__.py +2 -1
  3. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/cli/headless.py +2 -16
  4. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/command.py +1 -0
  5. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/config.py +4 -0
  6. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/context.py +5 -1
  7. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/message.py +23 -13
  8. codex_agent_framework-0.1.62/codex_agent/prompts/compaction_prompt.txt +17 -0
  9. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/service.py +5 -0
  10. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/sessions.py +3 -1
  11. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/status.py +9 -3
  12. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/stream_utils.py +0 -6
  13. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/tui/status_bar.py +35 -10
  14. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/utils.py +6 -10
  15. codex_agent_framework-0.1.62/codex_agent/version.py +1 -0
  16. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62/codex_agent_framework.egg-info}/PKG-INFO +1 -1
  17. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent_framework.egg-info/SOURCES.txt +1 -0
  18. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/pyproject.toml +1 -1
  19. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_agent_context.py +28 -3
  20. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_agent_plugins.py +1 -0
  21. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_agent_response.py +1 -0
  22. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_builtin_config.py +30 -1
  23. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_chat.py +20 -4
  24. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_cli_headless.py +26 -0
  25. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_messages.py +9 -7
  26. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_service.py +4 -0
  27. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_token_counter.py +11 -7
  28. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_utils.py +12 -0
  29. codex_agent_framework-0.1.60/codex_agent/version.py +0 -1
  30. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/CHANGELOG.md +0 -0
  31. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/LICENSE +0 -0
  32. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/MANIFEST.in +0 -0
  33. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/README.md +0 -0
  34. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/__init__.py +0 -0
  35. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/__main__.py +0 -0
  36. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/agent.py +0 -0
  37. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/agent_runtime.py +0 -0
  38. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/ai.py +0 -0
  39. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/browser.py +0 -0
  40. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_commands.py +0 -0
  41. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/__init__.py +0 -0
  42. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/bash/__init__.py +0 -0
  43. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/bash/requirements.txt +0 -0
  44. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/browser/__init__.py +0 -0
  45. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/browser/requirements.txt +0 -0
  46. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/content/__init__.py +0 -0
  47. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/content/requirements.txt +0 -0
  48. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/content/system.py +0 -0
  49. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/content/vision.py +0 -0
  50. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/desktop/__init__.py +0 -0
  51. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/desktop/requirements.txt +0 -0
  52. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/environment/__init__.py +0 -0
  53. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/environment/requirements.txt +0 -0
  54. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/files/__init__.py +0 -0
  55. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/files/requirements.txt +0 -0
  56. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/files/tools.py +0 -0
  57. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/image_generation/__init__.py +0 -0
  58. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/image_generation/image_generation_system_prompt.txt +0 -0
  59. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/image_generation/requirements.txt +0 -0
  60. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/interface/__init__.py +0 -0
  61. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/interface/requirements.txt +0 -0
  62. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/memory/__init__.py +0 -0
  63. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/memory/requirements.txt +0 -0
  64. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/planner/__init__.py +0 -0
  65. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/planner/requirements.txt +0 -0
  66. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/python/__init__.py +0 -0
  67. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/python/requirements.txt +0 -0
  68. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/realtime/__init__.py +0 -0
  69. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/realtime/audio.py +0 -0
  70. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/realtime/bridge.py +0 -0
  71. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/realtime/manager.py +0 -0
  72. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/realtime/requirements.txt +0 -0
  73. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/realtime/session.py +0 -0
  74. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/scheduler/__init__.py +0 -0
  75. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/scheduler/requirements.txt +0 -0
  76. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/subagents/__init__.py +0 -0
  77. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/subagents/profiles/explorer.json +0 -0
  78. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/subagents/requirements.txt +0 -0
  79. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/tts/__init__.py +0 -0
  80. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/tts/requirements.txt +0 -0
  81. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/web_search/__init__.py +0 -0
  82. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/cli/__init__.py +0 -0
  83. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/cli/main.py +0 -0
  84. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/cli/output.py +0 -0
  85. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/cli/root.py +0 -0
  86. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/cli/runner.py +0 -0
  87. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/client.py +0 -0
  88. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/desktop.py +0 -0
  89. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/event.py +0 -0
  90. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/get_text/__init__.py +0 -0
  91. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/get_text/default_gitignore +0 -0
  92. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/get_text/get_text.py +0 -0
  93. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/get_text/simpler_get_text.py +0 -0
  94. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/hooks.py +0 -0
  95. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/image.py +0 -0
  96. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/latex.py +0 -0
  97. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/mainloop.py +0 -0
  98. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/plugin.py +0 -0
  99. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/prompts/system_prompt.txt +0 -0
  100. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/provider.py +0 -0
  101. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/runtime.py +0 -0
  102. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/scheduler.py +0 -0
  103. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/scripts/install-system-dependencies.sh +0 -0
  104. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/server/__init__.py +0 -0
  105. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/server/app.py +0 -0
  106. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/server/core.py +0 -0
  107. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/stream.py +0 -0
  108. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/token_counter.py +0 -0
  109. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/tool.py +0 -0
  110. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/tray.py +0 -0
  111. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/tui/__init__.py +0 -0
  112. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/tui/app.py +0 -0
  113. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/tui/chat.py +0 -0
  114. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/tui/lifecycle.py +0 -0
  115. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/tui/log.py +0 -0
  116. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/tui/perf.py +0 -0
  117. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/tui/state.py +0 -0
  118. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/tui/stream_debug.py +0 -0
  119. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/tui/style.py +0 -0
  120. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/tui/transcript.py +0 -0
  121. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/tui/widgets.py +0 -0
  122. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/worker.py +0 -0
  123. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent_framework.egg-info/dependency_links.txt +0 -0
  124. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent_framework.egg-info/entry_points.txt +0 -0
  125. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent_framework.egg-info/requires.txt +0 -0
  126. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent_framework.egg-info/top_level.txt +0 -0
  127. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/dependencies.txt +0 -0
  128. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/setup.cfg +0 -0
  129. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_agent.py +0 -0
  130. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_agent_audio_hooks.py +0 -0
  131. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_agent_commands.py +0 -0
  132. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_agent_desktop.py +0 -0
  133. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_agent_environment.py +0 -0
  134. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_agent_image_generation.py +0 -0
  135. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_agent_observe.py +0 -0
  136. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_agent_providers_config.py +0 -0
  137. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_agent_realtime.py +0 -0
  138. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_agent_runtime.py +0 -0
  139. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_agent_sessions.py +0 -0
  140. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_agent_subagents.py +0 -0
  141. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_agent_tools.py +0 -0
  142. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_agent_turns.py +0 -0
  143. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_ai.py +0 -0
  144. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_browser.py +0 -0
  145. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_cli.py +0 -0
  146. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_cli_root.py +0 -0
  147. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_client.py +0 -0
  148. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_events.py +0 -0
  149. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_get_text_browser.py +0 -0
  150. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_hooks.py +0 -0
  151. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_image_message.py +0 -0
  152. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_local_desktop.py +0 -0
  153. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_memory.py +0 -0
  154. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_planner.py +0 -0
  155. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_scheduler.py +0 -0
  156. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_server.py +0 -0
  157. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_tui.py +0 -0
  158. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_worker.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codex-agent-framework
3
- Version: 0.1.60
3
+ Version: 0.1.62
4
4
  Summary: A lightweight event-driven Codex agent runtime.
5
5
  Author: Baptiste
6
6
  License-Expression: MIT
@@ -37,6 +37,7 @@ Tool result wrappers expose stable `wrapper_id` values in their XML representati
37
37
  vanished_wrapper_messages=status.get("vanished_wrapper_messages", 0),
38
38
  total_wrapper_messages=status.get("total_wrapper_messages", 0),
39
39
  base_context_tokens=status.get("base_context_tokens", status.used_tokens),
40
+ provider_context_tokens=status.get("provider_context_tokens", 0),
40
41
  active_wrapper_tokens=status.get("active_wrapper_tokens", 0),
41
42
  vanished_wrapper_tokens=status.get("vanished_wrapper_tokens", 0),
42
43
  compactable_turns=status.get("compactable_turns", 0),
@@ -57,7 +58,7 @@ Tool result wrappers expose stable `wrapper_id` values in their XML representati
57
58
  return "\n".join([
58
59
  "Context status:",
59
60
  f"- tokens: {status.used_tokens}/{status.input_token_limit} ({status.percent:.2f}%)",
60
- f"- token split base/active_wrappers/vanished_wrappers: {status.base_context_tokens}/{status.active_wrapper_tokens}/{status.vanished_wrapper_tokens}",
61
+ f"- token split base/vanished_wrappers/active_wrappers/providers: {status.base_context_tokens}/{status.vanished_wrapper_tokens}/{status.active_wrapper_tokens}/{status.provider_context_tokens}",
61
62
  f"- messages sent/total: {status.sent_session_messages}/{status.total_session_messages}",
62
63
  f"- persistent/temporary context messages: {status.persistent_session_messages}/{status.temporary_session_messages}",
63
64
  f"- wrappers active/vanished/total: {status.active_wrapper_messages}/{status.vanished_wrapper_messages}/{status.total_wrapper_messages}",
@@ -1,7 +1,8 @@
1
- import json
2
1
  import sys
3
2
  import threading
4
3
 
4
+ from codex_agent.config import parse_config_value
5
+
5
6
  from .output import dump_ndjson, output_payload, key_value_text
6
7
  from .runner import in_process_runner, process_runner, server_runner
7
8
 
@@ -71,21 +72,6 @@ def run_tools(args, extra_args=None):
71
72
  return payload
72
73
 
73
74
 
74
- def parse_config_value(value):
75
- text = str(value)
76
- lowered = text.lower()
77
- if lowered == "true":
78
- return True
79
- if lowered == "false":
80
- return False
81
- if lowered == "null" or lowered == "none":
82
- return None
83
- try:
84
- return json.loads(text)
85
- except json.JSONDecodeError:
86
- return value
87
-
88
-
89
75
  def parse_config_set_args(extra_args):
90
76
  args = list(extra_args or [])
91
77
  if not args:
@@ -33,6 +33,7 @@ def command(func=None, **options):
33
33
 
34
34
 
35
35
  def command_options(func):
36
+ """Return command decorator options from a function or bound method."""
36
37
  options = getattr(func, COMMAND_MARKER, None)
37
38
  if options is not None:
38
39
  return options
@@ -12,9 +12,12 @@ from .utils import runtime_join, text_content
12
12
 
13
13
  DEFAULT_SYSTEM_PROMPT_PATH = os.path.join(os.path.dirname(__file__), "prompts", "system_prompt.txt")
14
14
  DEFAULT_SYSTEM_PROMPT = text_content(DEFAULT_SYSTEM_PROMPT_PATH)
15
+ DEFAULT_COMPACTION_PROMPT_PATH = os.path.join(os.path.dirname(__file__), "prompts", "compaction_prompt.txt")
16
+ DEFAULT_COMPACTION_PROMPT = text_content(DEFAULT_COMPACTION_PROMPT_PATH)
15
17
 
16
18
 
17
19
  def parse_config_value(value):
20
+ """Parse CLI/config scalar strings into bool, null, numeric, or JSON values."""
18
21
  if isinstance(value, bool):
19
22
  return value
20
23
  if not isinstance(value, str):
@@ -82,6 +85,7 @@ class AgentConfig(Config):
82
85
 
83
86
  model = "gpt-5.4"
84
87
  compaction_model = "gpt-5.4-mini"
88
+ compaction_instructions = DEFAULT_COMPACTION_PROMPT
85
89
  system = DEFAULT_SYSTEM_PROMPT
86
90
  auto_proceed = True
87
91
  openai_api_key = None
@@ -14,6 +14,7 @@ class ContextManager:
14
14
  def __init__(self, agent):
15
15
  self.agent = agent
16
16
  self.token_count_coef = 1.0
17
+ self.last_provider_tokens = 0
17
18
 
18
19
  def provider_context_wrapper(self, provider_name, content):
19
20
  return f'<context_provider name="{provider_name}">\n{content}\n</context_provider>'
@@ -85,6 +86,8 @@ class ContextManager:
85
86
  system = [self.agent.config_manager.system_message()]
86
87
  response_tools = self.agent.tools_manager.get_response_tools()
87
88
  providers_msgs = self.get_providers_messages()
89
+ provider_tokens = self.response_input_token_count(providers_msgs, response_tools=[])
90
+ self.last_provider_tokens = provider_tokens
88
91
  max_images = self.agent.config.get("max_images", 10)
89
92
 
90
93
  if self.agent.config.prune_orphaned_tool_outputs_on_context_build:
@@ -139,7 +142,7 @@ class ContextManager:
139
142
  active_context_wrappers = [msg for msg in history if isinstance(msg, ToolResultWrapper)]
140
143
  active_wrapper_tokens = wrapper_token_count(active_context_wrappers)
141
144
  vanished_wrapper_tokens = wrapper_token_count(vanished_wrappers)
142
- base_context_tokens = max(0, current_count - active_wrapper_tokens)
145
+ base_context_tokens = max(0, current_count - provider_tokens - active_wrapper_tokens)
143
146
  if self.agent.session:
144
147
  compaction_plan = self.agent.session.compaction_plan(
145
148
  max_input_tokens=max_input_tokens,
@@ -174,6 +177,7 @@ class ContextManager:
174
177
  vanished_wrapper_messages=len(vanished_wrappers),
175
178
  total_wrapper_messages=len(wrappers),
176
179
  base_context_tokens=base_context_tokens,
180
+ provider_context_tokens=provider_tokens,
177
181
  active_wrapper_tokens=active_wrapper_tokens,
178
182
  vanished_wrapper_tokens=vanished_wrapper_tokens,
179
183
  compactable_turns=compactable_turns,
@@ -259,7 +259,7 @@ class CompactionMessage(DeveloperMessage):
259
259
  name = "compaction"
260
260
  content = "Previous conversation context was compacted by the backend."
261
261
  response_id = ""
262
- summaries = modict.factory(list)
262
+ output = modict.factory(list)
263
263
  usage = modict.factory(modict)
264
264
  compacted_message_count = 0
265
265
 
@@ -268,8 +268,12 @@ class CompactionMessage(DeveloperMessage):
268
268
  super().__init__(*args, **kwargs)
269
269
  if output_items is None:
270
270
  output_items = self.pop("output_items", None)
271
- if output_items is not None and not self.get("summaries"):
272
- self.summaries = self.summary_items(output_items)
271
+ legacy_summaries = self.pop("summaries", None)
272
+ if not self.get("output"):
273
+ if output_items is not None:
274
+ self.output = list(output_items or [])
275
+ elif legacy_summaries is not None:
276
+ self.output = list(legacy_summaries or [])
273
277
 
274
278
  @staticmethod
275
279
  def summary_items(items):
@@ -278,8 +282,12 @@ class CompactionMessage(DeveloperMessage):
278
282
  if item.get("type") == "compaction_summary"
279
283
  ]
280
284
 
285
+ @property
286
+ def summaries(self):
287
+ return self.summary_items(self.get("output") or [])
288
+
281
289
  def to_response_format(self):
282
- return list(self.get("summaries") or [])
290
+ return list(self.get("output") or [])
283
291
 
284
292
  def observed_token_count(self):
285
293
  usage = self.get("usage") or {}
@@ -306,7 +314,7 @@ class CompactionMessage(DeveloperMessage):
306
314
 
307
315
  def token_count_payload(self):
308
316
  sanitized = []
309
- for item in self.get("summaries") or []:
317
+ for item in self.get("output") or []:
310
318
  entry = dict(item)
311
319
  if entry.get("type") == "compaction_summary" and entry.get("encrypted_content"):
312
320
  entry["encrypted_content"] = "<encrypted_compaction_summary>"
@@ -320,17 +328,19 @@ class CompactionMessage(DeveloperMessage):
320
328
  return self.format_as_string_attrs(attrs)
321
329
 
322
330
  def as_string_body(self):
323
- summary = next(
324
- iter(self.get("summaries") or []),
325
- None,
326
- )
327
- if not summary:
331
+ summaries = self.summary_items(self.get("output") or [])
332
+ if not summaries:
328
333
  return self.content
334
+ summary_lines = "\n".join(
335
+ f"<compaction_summary id={summary.get('id', '')!r}>opaque backend summary</compaction_summary>"
336
+ for summary in summaries
337
+ )
338
+ output_count = len(self.get("output") or [])
329
339
  return dedent(
330
340
  f"""{self.content}
331
- <compaction_summary id={summary.get('id', '')!r}>
332
- opaque backend summary
333
- </compaction_summary>"""
341
+ <compaction_output items={output_count}>
342
+ {summary_lines}
343
+ </compaction_output>"""
334
344
  )
335
345
 
336
346
 
@@ -0,0 +1,17 @@
1
+ You are the standalone compaction model for an agentic coding assistant conversation.
2
+
3
+ Goal: replace the provided conversation history with the smallest useful compacted history that still lets the agent continue the task safely and coherently.
4
+
5
+ Instructions:
6
+ - Reduce the visible content of each retained message as aggressively as possible.
7
+ - Keep only information that is useful for future continuation: user goals, decisions, constraints, file paths, commands/results that matter, bugs found, fixes applied, validation status, unresolved questions, TODOs, and exact identifiers such as versions, commits, tags, job ids, paths, APIs, function/class names, and error messages.
8
+ - Remove chatter, repeated confirmations, verbose tool output, incidental logs, redundant reasoning, and obsolete branches of investigation.
9
+ - Preserve chronology and causality when it affects future decisions.
10
+ - The history to compact will often begin with output from a previous compaction. Treat that previous compacted context as important long-range background: carry forward its still-relevant facts, decisions, constraints, unresolved TODOs, and exact identifiers into the new final compaction, further compressed if needed, so repeated compactions keep a durable trace of distant context instead of replacing it with only the newest turns.
11
+ - Preserve the distinction between facts observed from tools/source-of-truth and inferences or hypotheses.
12
+ - Do not invent missing facts. If something is uncertain, mark it as uncertain.
13
+ - If tool outputs were summarized, keep the actionable conclusions and any exact values needed later, not the full raw output.
14
+ - The encrypted compaction summary should be dense, explicit, and highly informative. It must help the agent resume without relying on external memory/RAG.
15
+ - Prefer compact bullet-like factual wording over prose.
16
+
17
+ Return the compacted output in the format required by the compaction endpoint.
@@ -11,15 +11,18 @@ from .utils import project_python_env, runtime_join
11
11
 
12
12
  SERVER_SERVICE_NAME = "codex-agent.service"
13
13
  TRAY_SERVICE_NAME = "codex-agent-tray.service"
14
+ # Backwards-compatible alias for callers that predate the tray/server split.
14
15
  SERVICE_NAME = SERVER_SERVICE_NAME
15
16
  RESTART_WAKEUP_FILE = "restart_wakeup.json"
16
17
 
17
18
 
18
19
  def restart_wakeup_file():
20
+ """Return the runtime file used to resume work after a service restart."""
19
21
  return Path(runtime_join(RESTART_WAKEUP_FILE))
20
22
 
21
23
 
22
24
  def write_restart_wakeup(prompt, title=""):
25
+ """Persist a prompt for the next server startup to consume as a wakeup."""
23
26
  if not prompt or not str(prompt).strip():
24
27
  raise ValueError("prompt must not be empty")
25
28
  path = restart_wakeup_file()
@@ -33,6 +36,7 @@ def write_restart_wakeup(prompt, title=""):
33
36
 
34
37
 
35
38
  def pop_restart_wakeup():
39
+ """Read and remove any pending post-restart wakeup payload."""
36
40
  path = restart_wakeup_file()
37
41
  if not path.is_file():
38
42
  return None
@@ -47,6 +51,7 @@ def pop_restart_wakeup():
47
51
 
48
52
 
49
53
  def restart_server_service(check=False):
54
+ """Restart only the local server service without blocking the caller."""
50
55
  return service_controller().restart(SERVER_SERVICE_NAME, check=check, no_block=True)
51
56
 
52
57
 
@@ -183,7 +183,7 @@ class AgentSession(modict):
183
183
  usage = dict(usage)
184
184
  message = CompactionMessage(
185
185
  response_id=result.id,
186
- summaries=CompactionMessage.summary_items(result.output),
186
+ output=list(result.output or []),
187
187
  usage=usage or {},
188
188
  compacted_message_count=len(compacted_messages),
189
189
  compacted_turn_count=plan.compacted_turn_count,
@@ -832,6 +832,8 @@ class SessionsManager:
832
832
  model=model,
833
833
  max_input_tokens=max_input_tokens,
834
834
  )
835
+ if instructions is None:
836
+ instructions = self.agent.config.get("compaction_instructions")
835
837
  if instructions is not None:
836
838
  compact_kwargs.instructions = instructions
837
839
  if max_compacted_turns is not None:
@@ -33,6 +33,7 @@ class StatusManager:
33
33
  vanished_wrapper_messages=context_status.get("vanished_wrapper_messages", 0),
34
34
  total_wrapper_messages=context_status.get("total_wrapper_messages", 0),
35
35
  base_context_tokens=context_status.get("base_context_tokens", context_status.used_tokens),
36
+ provider_context_tokens=context_status.get("provider_context_tokens", 0),
36
37
  active_wrapper_tokens=context_status.get("active_wrapper_tokens", 0),
37
38
  vanished_wrapper_tokens=context_status.get("vanished_wrapper_tokens", 0),
38
39
  compactable_turns=context_status.get("compactable_turns", 0),
@@ -89,8 +90,9 @@ class StatusManager:
89
90
  msg_copy.content = truncate(msg_copy.content, max_tokens=max_input_tokens, model=self.agent.config.model)
90
91
  truncated_others.append(msg_copy)
91
92
 
93
+ provider_tokens = int(getattr(context_manager, "last_provider_tokens", 0) or 0)
92
94
  fixed_tokens = context_manager.response_input_token_count(system + images, response_tools=response_tools)
93
- current_count = fixed_tokens
95
+ current_count = fixed_tokens + provider_tokens
94
96
  history_start = len(truncated_others)
95
97
  for i, msg in enumerate(reversed(truncated_others)):
96
98
  msg_count = context_manager.response_input_token_count([msg], response_tools=[])
@@ -131,7 +133,7 @@ class StatusManager:
131
133
  active_context_wrappers = [msg for msg in included_others if isinstance(msg, ToolResultWrapper)]
132
134
  active_wrapper_tokens = wrapper_token_count(active_context_wrappers)
133
135
  vanished_wrapper_tokens = wrapper_token_count(vanished_wrappers)
134
- base_context_tokens = max(0, current_count - active_wrapper_tokens)
136
+ base_context_tokens = max(0, current_count - provider_tokens - active_wrapper_tokens)
135
137
  if self.agent.session:
136
138
  compaction_plan = self.agent.session.compaction_plan(
137
139
  max_input_tokens=max_input_tokens,
@@ -151,6 +153,7 @@ class StatusManager:
151
153
  input_token_limit=input_limit,
152
154
  percent=percent,
153
155
  fixed_tokens=fixed_tokens,
156
+ provider_context_tokens=provider_tokens,
154
157
  tool_spec_tokens=context_manager.response_tools_token_count(response_tools),
155
158
  token_count_source="local_estimate",
156
159
  visible_history_messages=visible_history_count,
@@ -213,6 +216,7 @@ class StatusManager:
213
216
  vanished_wrapper_messages=estimated.get("vanished_wrapper_messages", 0),
214
217
  total_wrapper_messages=estimated.get("total_wrapper_messages", 0),
215
218
  base_context_tokens=estimated.get("base_context_tokens", estimated.used_tokens),
219
+ provider_context_tokens=estimated.get("provider_context_tokens", 0),
216
220
  active_wrapper_tokens=estimated.get("active_wrapper_tokens", 0),
217
221
  vanished_wrapper_tokens=estimated.get("vanished_wrapper_tokens", 0),
218
222
  compactable_turns=estimated.get("compactable_turns", 0),
@@ -247,6 +251,7 @@ class StatusManager:
247
251
  vanished_wrapper_messages=estimated.get("vanished_wrapper_messages", 0),
248
252
  total_wrapper_messages=estimated.get("total_wrapper_messages", 0),
249
253
  base_context_tokens=estimated.get("base_context_tokens", estimated.used_tokens),
254
+ provider_context_tokens=estimated.get("provider_context_tokens", 0),
250
255
  active_wrapper_tokens=estimated.get("active_wrapper_tokens", 0),
251
256
  vanished_wrapper_tokens=estimated.get("vanished_wrapper_tokens", 0),
252
257
  compactable_turns=estimated.get("compactable_turns", 0),
@@ -282,7 +287,8 @@ class StatusManager:
282
287
  active_wrapper_messages=context_status.get("active_wrapper_messages", 0),
283
288
  vanished_wrapper_messages=context_status.get("vanished_wrapper_messages", 0),
284
289
  total_wrapper_messages=context_status.get("total_wrapper_messages", 0),
285
- base_context_tokens=max(0, input_tokens - context_status.get("active_wrapper_tokens", 0)),
290
+ base_context_tokens=max(0, input_tokens - context_status.get("provider_context_tokens", 0) - context_status.get("active_wrapper_tokens", 0)),
291
+ provider_context_tokens=context_status.get("provider_context_tokens", 0),
286
292
  active_wrapper_tokens=context_status.get("active_wrapper_tokens", 0),
287
293
  vanished_wrapper_tokens=context_status.get("vanished_wrapper_tokens", 0),
288
294
  compactable_turns=context_status.get("compactable_turns", 0),
@@ -6,12 +6,6 @@ import re
6
6
  from typing import Union
7
7
  import json
8
8
 
9
- def log(*data):
10
- if False:
11
- print(*data)
12
- input()
13
-
14
-
15
9
  class END:
16
10
  #sentinel value for end of stream
17
11
  pass
@@ -26,25 +26,50 @@ class StatusBar:
26
26
 
27
27
  @classmethod
28
28
  def _context_token_segments(cls, status, used, limit):
29
- if "base_context_tokens" not in status and "active_wrapper_tokens" not in status and "vanished_wrapper_tokens" not in status:
29
+ if (
30
+ "base_context_tokens" not in status
31
+ and "provider_context_tokens" not in status
32
+ and "active_wrapper_tokens" not in status
33
+ and "vanished_wrapper_tokens" not in status
34
+ ):
30
35
  return ((f"{used}/{limit} ktok", "#8b949e on #161b22"),)
31
- return (
32
- (cls._format_ktok(status.get("base_context_tokens") or 0), "#c9d1d9 on #161b22"),
33
- (f"+{cls._format_ktok(status.get('active_wrapper_tokens') or 0)}", "italic #f0883e on #161b22"),
34
- (f"+{cls._format_ktok(status.get('vanished_wrapper_tokens') or 0)}", "italic #6e7681 on #161b22"),
36
+ base_tokens = status.get("base_context_tokens") or 0
37
+ provider_tokens = status.get("provider_context_tokens") or 0
38
+ active_wrapper_tokens = status.get("active_wrapper_tokens") or 0
39
+ vanished_wrapper_tokens = status.get("vanished_wrapper_tokens") or 0
40
+ active_total = cls._format_ktok(base_tokens + provider_tokens + active_wrapper_tokens)
41
+ segments = [
42
+ (cls._format_ktok(base_tokens), "#c9d1d9 on #161b22"),
43
+ ]
44
+ segments.extend([
45
+ (f"(+{cls._format_ktok(vanished_wrapper_tokens)})", "italic #6e7681 on #161b22"),
46
+ (f"+{cls._format_ktok(active_wrapper_tokens)}", "italic #f0883e on #161b22"),
47
+ ])
48
+ if provider_tokens:
49
+ segments.append((f"+{cls._format_ktok(provider_tokens)}", "italic #58a6ff on #161b22"))
50
+ segments.extend([
51
+ ("=", "#8b949e on #161b22"),
52
+ (active_total, "#c9d1d9 on #161b22"),
35
53
  (f"/{limit} ktok", "#8b949e on #161b22"),
36
- )
54
+ ])
55
+ return tuple(segments)
37
56
 
38
57
  @classmethod
39
58
  def _message_segments(cls, status):
40
59
  sent = int(status.get("sent_session_messages") or 0)
41
60
  total = int(status.get("total_session_messages") or 0)
42
61
  if "active_wrapper_messages" in status or "vanished_wrapper_messages" in status:
62
+ persistent = int(status.get("persistent_session_messages") or 0)
63
+ active_wrappers = int(status.get("active_wrapper_messages") or 0)
64
+ vanished_wrappers = int(status.get("vanished_wrapper_messages") or 0)
65
+ active_total = persistent + active_wrappers
43
66
  return (
44
- (str(int(status.get("persistent_session_messages") or 0)), "#c9d1d9 on #161b22"),
45
- (f"+{int(status.get('active_wrapper_messages') or 0)}", "italic #f0883e on #161b22"),
46
- (f"+{int(status.get('vanished_wrapper_messages') or 0)}", "italic #6e7681 on #161b22"),
47
- (f"/{total}", "#c9d1d9 on #161b22"),
67
+ (str(persistent), "#c9d1d9 on #161b22"),
68
+ (f"(+{vanished_wrappers})", "italic #6e7681 on #161b22"),
69
+ (f"+{active_wrappers}", "italic #f0883e on #161b22"),
70
+ ("=", "#8b949e on #161b22"),
71
+ (str(active_total), "#c9d1d9 on #161b22"),
72
+ (f"/{total}", "#8b949e on #161b22"),
48
73
  )
49
74
  if "persistent_session_messages" in status or "temporary_session_messages" in status:
50
75
  return (
@@ -262,6 +262,7 @@ def truncate(string, max_tokens=2000, start_line=1, model=None):
262
262
  return result
263
263
 
264
264
  def pack_msgs(messages):
265
+ """Format named text messages into a compact transcript string."""
265
266
  return "".join(f"{message.name}:\n{message.content.strip()}\n\n" for message in messages)
266
267
 
267
268
  def msg_token_count(msg, model=None):
@@ -318,20 +319,15 @@ def split_history_turns(messages):
318
319
  return turns, current
319
320
 
320
321
  def find_pattern(text, pattern=None):
321
- """
322
- Renvoie une liste de tuples contenant TOUS les groupes capturés par le pattern.
322
+ """Return captured regex groups for every match in ``text``.
323
323
 
324
- - Si le pattern contient 1 groupe -> tuple de 1 élément
325
- - Si le pattern contient n groupes -> tuple de n éléments
324
+ The default pattern extracts fenced Python code blocks. Each result is a
325
+ tuple containing all capture groups for one match, even when there is only
326
+ one group.
326
327
  """
327
-
328
328
  pattern = pattern or r'```python(.*?)```'
329
329
  iterator = re.finditer(pattern, text, re.DOTALL)
330
-
331
- return [
332
- tuple(match.groups()) # groups() renvoie *tous* les groupes capturés
333
- for match in iterator
334
- ]
330
+ return [tuple(match.groups()) for match in iterator]
335
331
 
336
332
  def format(string, context=None):
337
333
  # Si aucun contexte n'est fourni, utiliser un dictionnaire vide
@@ -0,0 +1 @@
1
+ __version__ = "0.1.62"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codex-agent-framework
3
- Version: 0.1.60
3
+ Version: 0.1.62
4
4
  Summary: A lightweight event-driven Codex agent runtime.
5
5
  Author: Baptiste
6
6
  License-Expression: MIT
@@ -89,6 +89,7 @@ codex_agent/get_text/__init__.py
89
89
  codex_agent/get_text/default_gitignore
90
90
  codex_agent/get_text/get_text.py
91
91
  codex_agent/get_text/simpler_get_text.py
92
+ codex_agent/prompts/compaction_prompt.txt
92
93
  codex_agent/prompts/system_prompt.txt
93
94
  codex_agent/scripts/install-system-dependencies.sh
94
95
  codex_agent/server/__init__.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "codex-agent-framework"
7
- version = "0.1.60"
7
+ version = "0.1.62"
8
8
  description = "A lightweight event-driven Codex agent runtime."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -103,7 +103,10 @@ def test_agent_session_compact_purges_compacted_prefix_before_remainder(tmp_path
103
103
  assert session.messages == [message, current]
104
104
  assert [msg.id for msg in AgentSession.load(session.session_id).messages] == [message.id, current.id]
105
105
  assert "output_items" not in message
106
+ assert "summaries" not in message
107
+ assert message.output == FakeCompactionResult.output
106
108
  assert message.summaries == [FakeCompactionResult.output[1]]
109
+ assert message.to_response_format() == FakeCompactionResult.output
107
110
  assert message.usage == FakeCompactionResult.usage
108
111
  assert message.count_tokens(model="gpt-5") == 12000
109
112
  assert message.compacted_message_count == 2
@@ -358,7 +361,7 @@ def test_agent_get_response_auto_compact_failure_continues_backend_call(monkeypa
358
361
  assert not any(isinstance(message, CompactionMessage) for message in agent.session.messages)
359
362
 
360
363
 
361
- def test_agent_compact_session_uses_mini_model_by_default():
364
+ def test_agent_compact_session_uses_mini_model_and_instructions_by_default():
362
365
  agent = Agent(session="new")
363
366
  visible = DeveloperMessage(content="visible compactable context")
364
367
  done = AssistantMessage(content="done")
@@ -370,7 +373,8 @@ def test_agent_compact_session_uses_mini_model_by_default():
370
373
 
371
374
  assert isinstance(message, CompactionMessage)
372
375
  assert client.params["model"] == "gpt-5.4-mini"
373
- assert "instructions" not in client.params
376
+ assert "smallest useful compacted history" in client.params["instructions"]
377
+ assert "encrypted compaction summary" in client.params["instructions"]
374
378
 
375
379
 
376
380
  def test_agent_compact_session_respects_explicit_model_override():
@@ -401,6 +405,21 @@ def test_agent_compact_session_respects_explicit_instructions_override():
401
405
  assert client.params["instructions"] == "Summarize faithfully."
402
406
 
403
407
 
408
+ def test_agent_compact_session_can_disable_default_instructions():
409
+ agent = Agent(session="new")
410
+ agent.config.compaction_instructions = None
411
+ visible = DeveloperMessage(content="visible compactable context")
412
+ done = AssistantMessage(content="done")
413
+ current = DeveloperMessage(content="current unfinished turn")
414
+ agent.session.messages = [visible, done, current]
415
+ client = FakeCompactionClient()
416
+
417
+ message = agent.compact_session(client=client)
418
+
419
+ assert isinstance(message, CompactionMessage)
420
+ assert "instructions" not in client.params
421
+
422
+
404
423
  def test_slash_compact_reports_compaction_failure(monkeypatch):
405
424
  agent = Agent(session="new")
406
425
  monkeypatch.setattr(agent, "compact_session", lambda **_kwargs: modict(status="failed", error="boom"))
@@ -472,18 +491,24 @@ def test_agent_tool_result_messages_append_directly_without_pending():
472
491
  def test_agent_get_context_starts_at_latest_compaction():
473
492
  agent = Agent(session="new")
474
493
  old = DeveloperMessage(content="old hidden context")
475
- previous = CompactionMessage(output_items=[{"type": "compaction_summary", "id": "cs_123"}])
494
+ previous_output = [
495
+ {"type": "message", "role": "developer", "content": [{"type": "input_text", "text": "compacted visible anchor"}]},
496
+ {"type": "compaction_summary", "id": "cs_123", "encrypted_content": "opaque"},
497
+ ]
498
+ previous = CompactionMessage(output=previous_output)
476
499
  current = DeveloperMessage(content="current visible context")
477
500
  agent.session.messages = [old, previous, current]
478
501
 
479
502
  context = agent.context_manager.get_context()
480
503
  context_text = "\n".join(str(message.get("content", "")) for message in context)
504
+ payload = agent.context_manager.response_input_token_payload(context)
481
505
 
482
506
  context_ids = [message.id for message in context]
483
507
  assert previous.id in context_ids
484
508
  assert current.id in context_ids
485
509
  assert old.id not in context_ids
486
510
  assert "old hidden context" not in context_text
511
+ assert payload.input[:2] == previous_output
487
512
 
488
513
 
489
514
  def test_agent_local_context_estimate_does_not_count_encrypted_compaction_payload():
@@ -387,6 +387,7 @@ def test_context_plugin_exposes_monitor_control_tools_and_wrapper_pruning():
387
387
  assert status.temporary_session_messages >= 3
388
388
  monitor = agent.providers.context_status_monitor()
389
389
  assert "Context status:" in monitor
390
+ assert "token split base/vanished_wrappers/active_wrappers/providers" in monitor
390
391
  assert "context_prune_wrappers" in monitor
391
392
 
392
393
  wrappers = agent.tools.context_list_wrappers()
@@ -204,6 +204,7 @@ def test_agent_get_response_auto_compacts_before_backend_call(monkeypatch):
204
204
  assert calls == [{
205
205
  "model": agent.config.compaction_model,
206
206
  "max_input_tokens": agent.config.max_input_tokens,
207
+ "instructions": agent.config.compaction_instructions,
207
208
  }]
208
209
  assert any(isinstance(message, CompactionMessage) for message in agent.ai.params["messages"])
209
210
 
@@ -1,4 +1,4 @@
1
- from codex_agent.config import ConfigManager
1
+ from codex_agent.config import ConfigManager, parse_config_value
2
2
 
3
3
 
4
4
  class DummyAgent:
@@ -12,6 +12,35 @@ def manager_with_builtin_field(field, value):
12
12
  return manager
13
13
 
14
14
 
15
+ def test_parse_config_value_handles_cli_scalars_and_json():
16
+ cases = {
17
+ "true": True,
18
+ "yes": True,
19
+ "on": True,
20
+ "1": True,
21
+ "false": False,
22
+ "no": False,
23
+ "off": False,
24
+ "0": False,
25
+ "none": None,
26
+ "null": None,
27
+ "128": 128,
28
+ "-7": -7,
29
+ "3.5": 3.5,
30
+ "1e3": 1000.0,
31
+ '{"model":"test"}': {"model": "test"},
32
+ '["memory"]': ["memory"],
33
+ }
34
+ for raw, expected in cases.items():
35
+ assert parse_config_value(raw) == expected
36
+
37
+
38
+ def test_parse_config_value_preserves_unknown_strings_and_values():
39
+ assert parse_config_value("gpt-test") == "gpt-test"
40
+ assert parse_config_value(True) is True
41
+ assert parse_config_value({"already": "parsed"}) == {"already": "parsed"}
42
+
43
+
15
44
  def test_builtin_plugin_enabled_none_loads_everything():
16
45
  manager = manager_with_builtin_field("builtin_plugins", None)
17
46