codex-agent-framework 0.1.60__tar.gz → 0.1.61__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.61}/PKG-INFO +1 -1
  2. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/builtin_plugins/context/__init__.py +2 -1
  3. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/config.py +3 -0
  4. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/context.py +5 -1
  5. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/message.py +23 -13
  6. codex_agent_framework-0.1.61/codex_agent/prompts/compaction_prompt.txt +17 -0
  7. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/sessions.py +3 -1
  8. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/status.py +9 -3
  9. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/tui/status_bar.py +35 -10
  10. codex_agent_framework-0.1.61/codex_agent/version.py +1 -0
  11. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61/codex_agent_framework.egg-info}/PKG-INFO +1 -1
  12. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent_framework.egg-info/SOURCES.txt +1 -0
  13. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/pyproject.toml +1 -1
  14. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/tests/test_agent_context.py +28 -3
  15. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/tests/test_agent_plugins.py +1 -0
  16. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/tests/test_agent_response.py +1 -0
  17. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/tests/test_chat.py +20 -4
  18. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/tests/test_messages.py +9 -7
  19. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/tests/test_token_counter.py +11 -7
  20. codex_agent_framework-0.1.60/codex_agent/version.py +0 -1
  21. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/CHANGELOG.md +0 -0
  22. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/LICENSE +0 -0
  23. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/MANIFEST.in +0 -0
  24. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/README.md +0 -0
  25. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/__init__.py +0 -0
  26. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/__main__.py +0 -0
  27. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/agent.py +0 -0
  28. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/agent_runtime.py +0 -0
  29. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/ai.py +0 -0
  30. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/browser.py +0 -0
  31. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/builtin_commands.py +0 -0
  32. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/builtin_plugins/__init__.py +0 -0
  33. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/builtin_plugins/bash/__init__.py +0 -0
  34. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/builtin_plugins/bash/requirements.txt +0 -0
  35. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/builtin_plugins/browser/__init__.py +0 -0
  36. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/builtin_plugins/browser/requirements.txt +0 -0
  37. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/builtin_plugins/content/__init__.py +0 -0
  38. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/builtin_plugins/content/requirements.txt +0 -0
  39. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/builtin_plugins/content/system.py +0 -0
  40. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/builtin_plugins/content/vision.py +0 -0
  41. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/builtin_plugins/desktop/__init__.py +0 -0
  42. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/builtin_plugins/desktop/requirements.txt +0 -0
  43. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/builtin_plugins/environment/__init__.py +0 -0
  44. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/builtin_plugins/environment/requirements.txt +0 -0
  45. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/builtin_plugins/files/__init__.py +0 -0
  46. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/builtin_plugins/files/requirements.txt +0 -0
  47. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/builtin_plugins/files/tools.py +0 -0
  48. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/builtin_plugins/image_generation/__init__.py +0 -0
  49. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/builtin_plugins/image_generation/image_generation_system_prompt.txt +0 -0
  50. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/builtin_plugins/image_generation/requirements.txt +0 -0
  51. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/builtin_plugins/interface/__init__.py +0 -0
  52. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/builtin_plugins/interface/requirements.txt +0 -0
  53. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/builtin_plugins/memory/__init__.py +0 -0
  54. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/builtin_plugins/memory/requirements.txt +0 -0
  55. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/builtin_plugins/planner/__init__.py +0 -0
  56. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/builtin_plugins/planner/requirements.txt +0 -0
  57. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/builtin_plugins/python/__init__.py +0 -0
  58. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/builtin_plugins/python/requirements.txt +0 -0
  59. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/builtin_plugins/realtime/__init__.py +0 -0
  60. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/builtin_plugins/realtime/audio.py +0 -0
  61. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/builtin_plugins/realtime/bridge.py +0 -0
  62. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/builtin_plugins/realtime/manager.py +0 -0
  63. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/builtin_plugins/realtime/requirements.txt +0 -0
  64. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/builtin_plugins/realtime/session.py +0 -0
  65. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/builtin_plugins/scheduler/__init__.py +0 -0
  66. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/builtin_plugins/scheduler/requirements.txt +0 -0
  67. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/builtin_plugins/subagents/__init__.py +0 -0
  68. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/builtin_plugins/subagents/profiles/explorer.json +0 -0
  69. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/builtin_plugins/subagents/requirements.txt +0 -0
  70. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/builtin_plugins/tts/__init__.py +0 -0
  71. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/builtin_plugins/tts/requirements.txt +0 -0
  72. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/builtin_plugins/web_search/__init__.py +0 -0
  73. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/cli/__init__.py +0 -0
  74. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/cli/headless.py +0 -0
  75. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/cli/main.py +0 -0
  76. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/cli/output.py +0 -0
  77. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/cli/root.py +0 -0
  78. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/cli/runner.py +0 -0
  79. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/client.py +0 -0
  80. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/command.py +0 -0
  81. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/desktop.py +0 -0
  82. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/event.py +0 -0
  83. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/get_text/__init__.py +0 -0
  84. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/get_text/default_gitignore +0 -0
  85. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/get_text/get_text.py +0 -0
  86. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/get_text/simpler_get_text.py +0 -0
  87. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/hooks.py +0 -0
  88. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/image.py +0 -0
  89. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/latex.py +0 -0
  90. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/mainloop.py +0 -0
  91. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/plugin.py +0 -0
  92. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/prompts/system_prompt.txt +0 -0
  93. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/provider.py +0 -0
  94. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/runtime.py +0 -0
  95. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/scheduler.py +0 -0
  96. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/scripts/install-system-dependencies.sh +0 -0
  97. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/server/__init__.py +0 -0
  98. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/server/app.py +0 -0
  99. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/server/core.py +0 -0
  100. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/service.py +0 -0
  101. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/stream.py +0 -0
  102. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/stream_utils.py +0 -0
  103. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/token_counter.py +0 -0
  104. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/tool.py +0 -0
  105. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/tray.py +0 -0
  106. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/tui/__init__.py +0 -0
  107. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/tui/app.py +0 -0
  108. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/tui/chat.py +0 -0
  109. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/tui/lifecycle.py +0 -0
  110. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/tui/log.py +0 -0
  111. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/tui/perf.py +0 -0
  112. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/tui/state.py +0 -0
  113. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/tui/stream_debug.py +0 -0
  114. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/tui/style.py +0 -0
  115. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/tui/transcript.py +0 -0
  116. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/tui/widgets.py +0 -0
  117. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/utils.py +0 -0
  118. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent/worker.py +0 -0
  119. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent_framework.egg-info/dependency_links.txt +0 -0
  120. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent_framework.egg-info/entry_points.txt +0 -0
  121. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent_framework.egg-info/requires.txt +0 -0
  122. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/codex_agent_framework.egg-info/top_level.txt +0 -0
  123. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/dependencies.txt +0 -0
  124. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/setup.cfg +0 -0
  125. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/tests/test_agent.py +0 -0
  126. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/tests/test_agent_audio_hooks.py +0 -0
  127. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/tests/test_agent_commands.py +0 -0
  128. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/tests/test_agent_desktop.py +0 -0
  129. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/tests/test_agent_environment.py +0 -0
  130. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/tests/test_agent_image_generation.py +0 -0
  131. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/tests/test_agent_observe.py +0 -0
  132. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/tests/test_agent_providers_config.py +0 -0
  133. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/tests/test_agent_realtime.py +0 -0
  134. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/tests/test_agent_runtime.py +0 -0
  135. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/tests/test_agent_sessions.py +0 -0
  136. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/tests/test_agent_subagents.py +0 -0
  137. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/tests/test_agent_tools.py +0 -0
  138. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/tests/test_agent_turns.py +0 -0
  139. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/tests/test_ai.py +0 -0
  140. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/tests/test_browser.py +0 -0
  141. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/tests/test_builtin_config.py +0 -0
  142. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/tests/test_cli.py +0 -0
  143. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/tests/test_cli_headless.py +0 -0
  144. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/tests/test_cli_root.py +0 -0
  145. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/tests/test_client.py +0 -0
  146. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/tests/test_events.py +0 -0
  147. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/tests/test_get_text_browser.py +0 -0
  148. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/tests/test_hooks.py +0 -0
  149. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/tests/test_image_message.py +0 -0
  150. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/tests/test_local_desktop.py +0 -0
  151. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/tests/test_memory.py +0 -0
  152. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/tests/test_planner.py +0 -0
  153. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/tests/test_scheduler.py +0 -0
  154. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/tests/test_server.py +0 -0
  155. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/tests/test_service.py +0 -0
  156. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/tests/test_tui.py +0 -0
  157. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/tests/test_utils.py +0 -0
  158. {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.61}/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.61
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}",
@@ -12,6 +12,8 @@ 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):
@@ -82,6 +84,7 @@ class AgentConfig(Config):
82
84
 
83
85
  model = "gpt-5.4"
84
86
  compaction_model = "gpt-5.4-mini"
87
+ compaction_instructions = DEFAULT_COMPACTION_PROMPT
85
88
  system = DEFAULT_SYSTEM_PROMPT
86
89
  auto_proceed = True
87
90
  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.
@@ -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),
@@ -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 (
@@ -0,0 +1 @@
1
+ __version__ = "0.1.61"
@@ -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.61
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.61"
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
 
@@ -285,7 +285,22 @@ def test_agent_context_status_counts_active_and_vanished_wrappers():
285
285
  assert status.active_wrapper_tokens > 0
286
286
  assert status.vanished_wrapper_tokens > 0
287
287
  assert status.base_context_tokens >= status.fixed_tokens
288
- assert status.used_tokens == status.base_context_tokens + status.active_wrapper_tokens
288
+ assert status.provider_context_tokens == 0
289
+ assert status.used_tokens == status.base_context_tokens + status.provider_context_tokens + status.active_wrapper_tokens
290
+
291
+
292
+ def test_agent_context_status_reuses_last_provider_tokens_in_visible_total():
293
+ agent = Agent(session="new", input_token_limit=10_000)
294
+ agent.context_manager.last_provider_tokens = 1_234
295
+ agent.session.messages = [DeveloperMessage(content="persistent context")]
296
+
297
+ status = agent.status_manager.context_status()
298
+
299
+ assert status.provider_context_tokens == 1_234
300
+ assert status.vanished_wrapper_tokens >= 0
301
+ assert status.used_tokens == status.base_context_tokens + status.provider_context_tokens + status.active_wrapper_tokens
302
+ assert status.used_tokens < status.base_context_tokens + status.provider_context_tokens + status.active_wrapper_tokens + status.vanished_wrapper_tokens or status.vanished_wrapper_tokens == 0
303
+ assert status.percent == status.used_tokens / 10_000 * 100
289
304
 
290
305
 
291
306
  def test_agent_status_preserves_persistent_and_temporary_message_counts():
@@ -557,6 +572,7 @@ def test_chat_status_bar_formats_context_and_message_wrapper_splits():
557
572
  context_limit_tokens=128_000,
558
573
  context_percent=9.609,
559
574
  base_context_tokens=10_000,
575
+ provider_context_tokens=3_000,
560
576
  active_wrapper_tokens=2_000,
561
577
  vanished_wrapper_tokens=4_000,
562
578
  sent_session_messages=43,
@@ -574,15 +590,15 @@ def test_chat_status_bar_formats_context_and_message_wrapper_splits():
574
590
  rendered = StatusBar.render(status)
575
591
  output = rendered.plain
576
592
 
577
- assert "ctx 10.0+2.0+4.0/128.0 ktok" in output
578
- assert "msg 33+7+12/93" in output
593
+ assert "ctx 10.0(+4.0)+2.0+3.0=15.0/128.0 ktok" in output
594
+ assert "msg 33(+12)+7=40/93" in output
579
595
  assert "cmp 17" in output
580
596
  assert any(
581
597
  "#f0883e" in str(span.style) and output[span.start:span.end] in {"+2.0", "+7"}
582
598
  for span in rendered.spans
583
599
  )
584
600
  assert any(
585
- "#6e7681" in str(span.style) and output[span.start:span.end] in {"+4.0", "+12"}
601
+ "#6e7681" in str(span.style) and output[span.start:span.end] in {"(+4.0)", "(+12)"}
586
602
  for span in rendered.spans
587
603
  )
588
604
 
@@ -214,18 +214,19 @@ def test_server_tool_response_message_as_string_includes_item_metadata():
214
214
  assert '"type": "web_search_call"' in text
215
215
 
216
216
 
217
- def test_compaction_message_replays_only_backend_summary_items():
218
- output_items = [
217
+ def test_compaction_message_replays_full_backend_output():
218
+ output = [
219
219
  {"type": "message", "role": "user", "content": [{"type": "input_text", "text": "old"}]},
220
220
  {"type": "compaction_summary", "id": "cs_123", "encrypted_content": "opaque"},
221
221
  ]
222
222
  message = CompactionMessage(
223
223
  response_id="resp_123",
224
- output_items=output_items,
224
+ output=output,
225
225
  compacted_message_count=4,
226
226
  )
227
227
 
228
- assert message.to_response_format() == [output_items[1]]
228
+ assert message.to_response_format() == output
229
+ assert message.summaries == [output[1]]
229
230
  text = message.as_string()
230
231
  assert text.startswith("<CompactionMessage ")
231
232
  assert "response_id='resp_123'" in text
@@ -244,13 +245,14 @@ def test_compaction_message_migrates_legacy_output_items_without_persisting_them
244
245
  message = CompactionMessage({"output_items": output_items})
245
246
 
246
247
  assert "output_items" not in message
248
+ assert message.output == output_items
247
249
  assert message.summaries == [output_items[1]]
248
- assert message.to_response_format() == [output_items[1]]
250
+ assert message.to_response_format() == output_items
249
251
 
250
252
 
251
253
  def test_compaction_message_prefers_output_usage_token_count():
252
254
  message = CompactionMessage(
253
- output_items=[{"type": "compaction_summary", "id": "cs_123", "encrypted_content": "opaque"}],
255
+ output=[{"type": "compaction_summary", "id": "cs_123", "encrypted_content": "opaque"}],
254
256
  usage={"input_tokens": 42897, "output_tokens": 12000, "total_tokens": 54912},
255
257
  )
256
258
 
@@ -260,7 +262,7 @@ def test_compaction_message_prefers_output_usage_token_count():
260
262
 
261
263
  def test_compaction_message_ignores_input_usage_as_future_context_size():
262
264
  message = CompactionMessage(
263
- output_items=[{"type": "compaction_summary", "id": "cs_123", "encrypted_content": "opaque"}],
265
+ output=[{"type": "compaction_summary", "id": "cs_123", "encrypted_content": "opaque"}],
264
266
  usage={"input_tokens": 50000, "max_input_tokens": 60000},
265
267
  )
266
268
 
@@ -147,7 +147,7 @@ def test_token_counter_prefers_observed_usage_for_compaction_message():
147
147
  ai = FakeAI()
148
148
  counter = TokenCounter(ai, default_model="gpt-5")
149
149
  message = CompactionMessage(
150
- output_items=[{"type": "compaction_summary", "id": "cs_123", "encrypted_content": "opaque"}],
150
+ output=[{"type": "compaction_summary", "id": "cs_123", "encrypted_content": "opaque"}],
151
151
  usage={"input_tokens": 400, "output_tokens": 120, "total_tokens": 520},
152
152
  )
153
153
 
@@ -162,7 +162,8 @@ def test_token_counter_falls_back_to_compaction_request_when_no_observed_usage()
162
162
  ai = FakeAI()
163
163
  counter = TokenCounter(ai, default_model="gpt-5")
164
164
  message = CompactionMessage(
165
- output_items=[{"type": "compaction_summary", "id": "cs_123", "encrypted_content": "opaque"}],
165
+ output=[{"type": "message", "role": "user", "content": [{"type": "input_text", "text": "old"}]},
166
+ {"type": "compaction_summary", "id": "cs_123", "encrypted_content": "opaque"}],
166
167
  )
167
168
 
168
169
  count = counter.count_object(message)
@@ -170,11 +171,14 @@ def test_token_counter_falls_back_to_compaction_request_when_no_observed_usage()
170
171
  assert count == 303
171
172
  assert ai.calls == [{
172
173
  "model": "gpt-5",
173
- "input": [{
174
- "type": "compaction_summary",
175
- "id": "cs_123",
176
- "encrypted_content": "<encrypted_compaction_summary>",
177
- }],
174
+ "input": [
175
+ {"type": "message", "role": "user", "content": [{"type": "input_text", "text": "old"}]},
176
+ {
177
+ "type": "compaction_summary",
178
+ "id": "cs_123",
179
+ "encrypted_content": "<encrypted_compaction_summary>",
180
+ },
181
+ ],
178
182
  }]
179
183
  assert message.token_count["gpt-5"]["count"] == 303
180
184
 
@@ -1 +0,0 @@
1
- __version__ = "0.1.60"