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.
- {codex_agent_framework-0.1.60/codex_agent_framework.egg-info → codex_agent_framework-0.1.62}/PKG-INFO +1 -1
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/context/__init__.py +2 -1
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/cli/headless.py +2 -16
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/command.py +1 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/config.py +4 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/context.py +5 -1
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/message.py +23 -13
- codex_agent_framework-0.1.62/codex_agent/prompts/compaction_prompt.txt +17 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/service.py +5 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/sessions.py +3 -1
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/status.py +9 -3
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/stream_utils.py +0 -6
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/tui/status_bar.py +35 -10
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/utils.py +6 -10
- codex_agent_framework-0.1.62/codex_agent/version.py +1 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62/codex_agent_framework.egg-info}/PKG-INFO +1 -1
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent_framework.egg-info/SOURCES.txt +1 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/pyproject.toml +1 -1
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_agent_context.py +28 -3
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_agent_plugins.py +1 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_agent_response.py +1 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_builtin_config.py +30 -1
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_chat.py +20 -4
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_cli_headless.py +26 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_messages.py +9 -7
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_service.py +4 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_token_counter.py +11 -7
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_utils.py +12 -0
- codex_agent_framework-0.1.60/codex_agent/version.py +0 -1
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/CHANGELOG.md +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/LICENSE +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/MANIFEST.in +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/README.md +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/__init__.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/__main__.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/agent.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/agent_runtime.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/ai.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/browser.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_commands.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/__init__.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/bash/__init__.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/bash/requirements.txt +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/browser/__init__.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/browser/requirements.txt +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/content/__init__.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/content/requirements.txt +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/content/system.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/content/vision.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/desktop/__init__.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/desktop/requirements.txt +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/environment/__init__.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/environment/requirements.txt +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/files/__init__.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/files/requirements.txt +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/files/tools.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/image_generation/__init__.py +0 -0
- {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
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/image_generation/requirements.txt +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/interface/__init__.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/interface/requirements.txt +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/memory/__init__.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/memory/requirements.txt +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/planner/__init__.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/planner/requirements.txt +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/python/__init__.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/python/requirements.txt +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/realtime/__init__.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/realtime/audio.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/realtime/bridge.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/realtime/manager.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/realtime/requirements.txt +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/realtime/session.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/scheduler/__init__.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/scheduler/requirements.txt +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/subagents/__init__.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/subagents/profiles/explorer.json +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/subagents/requirements.txt +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/tts/__init__.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/tts/requirements.txt +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/builtin_plugins/web_search/__init__.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/cli/__init__.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/cli/main.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/cli/output.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/cli/root.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/cli/runner.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/client.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/desktop.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/event.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/get_text/__init__.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/get_text/default_gitignore +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/get_text/get_text.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/get_text/simpler_get_text.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/hooks.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/image.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/latex.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/mainloop.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/plugin.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/prompts/system_prompt.txt +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/provider.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/runtime.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/scheduler.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/scripts/install-system-dependencies.sh +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/server/__init__.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/server/app.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/server/core.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/stream.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/token_counter.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/tool.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/tray.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/tui/__init__.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/tui/app.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/tui/chat.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/tui/lifecycle.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/tui/log.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/tui/perf.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/tui/state.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/tui/stream_debug.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/tui/style.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/tui/transcript.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/tui/widgets.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent/worker.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent_framework.egg-info/dependency_links.txt +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent_framework.egg-info/entry_points.txt +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent_framework.egg-info/requires.txt +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/codex_agent_framework.egg-info/top_level.txt +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/dependencies.txt +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/setup.cfg +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_agent.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_agent_audio_hooks.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_agent_commands.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_agent_desktop.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_agent_environment.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_agent_image_generation.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_agent_observe.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_agent_providers_config.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_agent_realtime.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_agent_runtime.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_agent_sessions.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_agent_subagents.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_agent_tools.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_agent_turns.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_ai.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_browser.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_cli.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_cli_root.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_client.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_events.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_get_text_browser.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_hooks.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_image_message.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_local_desktop.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_memory.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_planner.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_scheduler.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_server.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_tui.py +0 -0
- {codex_agent_framework-0.1.60 → codex_agent_framework-0.1.62}/tests/test_worker.py +0 -0
|
@@ -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/
|
|
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:
|
|
@@ -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
|
-
|
|
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
|
-
|
|
272
|
-
|
|
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("
|
|
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("
|
|
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
|
-
|
|
324
|
-
|
|
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
|
-
<
|
|
332
|
-
|
|
333
|
-
</
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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(
|
|
45
|
-
(f"+{
|
|
46
|
-
(f"+{
|
|
47
|
-
(
|
|
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
|
-
|
|
325
|
-
|
|
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"
|
|
@@ -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
|
|
@@ -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
|
|
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 "
|
|
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
|
-
|
|
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
|
|