codex-agent-framework 0.1.61__tar.gz → 0.1.63__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.61/codex_agent_framework.egg-info → codex_agent_framework-0.1.63}/PKG-INFO +1 -1
  2. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/agent_runtime.py +1 -1
  3. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/cli/headless.py +2 -16
  4. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/command.py +1 -0
  5. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/config.py +1 -0
  6. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/service.py +5 -0
  7. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/stream_utils.py +2 -8
  8. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/utils.py +15 -19
  9. codex_agent_framework-0.1.63/codex_agent/version.py +1 -0
  10. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63/codex_agent_framework.egg-info}/PKG-INFO +1 -1
  11. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/pyproject.toml +1 -1
  12. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/tests/test_agent_runtime.py +19 -0
  13. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/tests/test_builtin_config.py +30 -1
  14. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/tests/test_cli_headless.py +26 -0
  15. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/tests/test_service.py +4 -0
  16. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/tests/test_utils.py +33 -1
  17. codex_agent_framework-0.1.61/codex_agent/version.py +0 -1
  18. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/CHANGELOG.md +0 -0
  19. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/LICENSE +0 -0
  20. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/MANIFEST.in +0 -0
  21. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/README.md +0 -0
  22. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/__init__.py +0 -0
  23. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/__main__.py +0 -0
  24. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/agent.py +0 -0
  25. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/ai.py +0 -0
  26. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/browser.py +0 -0
  27. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/builtin_commands.py +0 -0
  28. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/builtin_plugins/__init__.py +0 -0
  29. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/builtin_plugins/bash/__init__.py +0 -0
  30. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/builtin_plugins/bash/requirements.txt +0 -0
  31. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/builtin_plugins/browser/__init__.py +0 -0
  32. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/builtin_plugins/browser/requirements.txt +0 -0
  33. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/builtin_plugins/content/__init__.py +0 -0
  34. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/builtin_plugins/content/requirements.txt +0 -0
  35. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/builtin_plugins/content/system.py +0 -0
  36. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/builtin_plugins/content/vision.py +0 -0
  37. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/builtin_plugins/context/__init__.py +0 -0
  38. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/builtin_plugins/desktop/__init__.py +0 -0
  39. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/builtin_plugins/desktop/requirements.txt +0 -0
  40. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/builtin_plugins/environment/__init__.py +0 -0
  41. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/builtin_plugins/environment/requirements.txt +0 -0
  42. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/builtin_plugins/files/__init__.py +0 -0
  43. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/builtin_plugins/files/requirements.txt +0 -0
  44. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/builtin_plugins/files/tools.py +0 -0
  45. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/builtin_plugins/image_generation/__init__.py +0 -0
  46. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/builtin_plugins/image_generation/image_generation_system_prompt.txt +0 -0
  47. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/builtin_plugins/image_generation/requirements.txt +0 -0
  48. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/builtin_plugins/interface/__init__.py +0 -0
  49. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/builtin_plugins/interface/requirements.txt +0 -0
  50. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/builtin_plugins/memory/__init__.py +0 -0
  51. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/builtin_plugins/memory/requirements.txt +0 -0
  52. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/builtin_plugins/planner/__init__.py +0 -0
  53. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/builtin_plugins/planner/requirements.txt +0 -0
  54. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/builtin_plugins/python/__init__.py +0 -0
  55. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/builtin_plugins/python/requirements.txt +0 -0
  56. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/builtin_plugins/realtime/__init__.py +0 -0
  57. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/builtin_plugins/realtime/audio.py +0 -0
  58. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/builtin_plugins/realtime/bridge.py +0 -0
  59. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/builtin_plugins/realtime/manager.py +0 -0
  60. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/builtin_plugins/realtime/requirements.txt +0 -0
  61. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/builtin_plugins/realtime/session.py +0 -0
  62. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/builtin_plugins/scheduler/__init__.py +0 -0
  63. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/builtin_plugins/scheduler/requirements.txt +0 -0
  64. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/builtin_plugins/subagents/__init__.py +0 -0
  65. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/builtin_plugins/subagents/profiles/explorer.json +0 -0
  66. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/builtin_plugins/subagents/requirements.txt +0 -0
  67. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/builtin_plugins/tts/__init__.py +0 -0
  68. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/builtin_plugins/tts/requirements.txt +0 -0
  69. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/builtin_plugins/web_search/__init__.py +0 -0
  70. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/cli/__init__.py +0 -0
  71. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/cli/main.py +0 -0
  72. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/cli/output.py +0 -0
  73. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/cli/root.py +0 -0
  74. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/cli/runner.py +0 -0
  75. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/client.py +0 -0
  76. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/context.py +0 -0
  77. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/desktop.py +0 -0
  78. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/event.py +0 -0
  79. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/get_text/__init__.py +0 -0
  80. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/get_text/default_gitignore +0 -0
  81. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/get_text/get_text.py +0 -0
  82. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/get_text/simpler_get_text.py +0 -0
  83. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/hooks.py +0 -0
  84. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/image.py +0 -0
  85. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/latex.py +0 -0
  86. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/mainloop.py +0 -0
  87. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/message.py +0 -0
  88. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/plugin.py +0 -0
  89. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/prompts/compaction_prompt.txt +0 -0
  90. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/prompts/system_prompt.txt +0 -0
  91. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/provider.py +0 -0
  92. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/runtime.py +0 -0
  93. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/scheduler.py +0 -0
  94. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/scripts/install-system-dependencies.sh +0 -0
  95. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/server/__init__.py +0 -0
  96. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/server/app.py +0 -0
  97. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/server/core.py +0 -0
  98. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/sessions.py +0 -0
  99. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/status.py +0 -0
  100. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/stream.py +0 -0
  101. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/token_counter.py +0 -0
  102. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/tool.py +0 -0
  103. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/tray.py +0 -0
  104. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/tui/__init__.py +0 -0
  105. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/tui/app.py +0 -0
  106. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/tui/chat.py +0 -0
  107. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/tui/lifecycle.py +0 -0
  108. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/tui/log.py +0 -0
  109. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/tui/perf.py +0 -0
  110. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/tui/state.py +0 -0
  111. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/tui/status_bar.py +0 -0
  112. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/tui/stream_debug.py +0 -0
  113. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/tui/style.py +0 -0
  114. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/tui/transcript.py +0 -0
  115. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/tui/widgets.py +0 -0
  116. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent/worker.py +0 -0
  117. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent_framework.egg-info/SOURCES.txt +0 -0
  118. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent_framework.egg-info/dependency_links.txt +0 -0
  119. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent_framework.egg-info/entry_points.txt +0 -0
  120. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent_framework.egg-info/requires.txt +0 -0
  121. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/codex_agent_framework.egg-info/top_level.txt +0 -0
  122. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/dependencies.txt +0 -0
  123. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/setup.cfg +0 -0
  124. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/tests/test_agent.py +0 -0
  125. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/tests/test_agent_audio_hooks.py +0 -0
  126. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/tests/test_agent_commands.py +0 -0
  127. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/tests/test_agent_context.py +0 -0
  128. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/tests/test_agent_desktop.py +0 -0
  129. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/tests/test_agent_environment.py +0 -0
  130. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/tests/test_agent_image_generation.py +0 -0
  131. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/tests/test_agent_observe.py +0 -0
  132. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/tests/test_agent_plugins.py +0 -0
  133. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/tests/test_agent_providers_config.py +0 -0
  134. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/tests/test_agent_realtime.py +0 -0
  135. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/tests/test_agent_response.py +0 -0
  136. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/tests/test_agent_sessions.py +0 -0
  137. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/tests/test_agent_subagents.py +0 -0
  138. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/tests/test_agent_tools.py +0 -0
  139. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/tests/test_agent_turns.py +0 -0
  140. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/tests/test_ai.py +0 -0
  141. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/tests/test_browser.py +0 -0
  142. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/tests/test_chat.py +0 -0
  143. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/tests/test_cli.py +0 -0
  144. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/tests/test_cli_root.py +0 -0
  145. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/tests/test_client.py +0 -0
  146. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/tests/test_events.py +0 -0
  147. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/tests/test_get_text_browser.py +0 -0
  148. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/tests/test_hooks.py +0 -0
  149. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/tests/test_image_message.py +0 -0
  150. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/tests/test_local_desktop.py +0 -0
  151. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/tests/test_memory.py +0 -0
  152. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/tests/test_messages.py +0 -0
  153. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/tests/test_planner.py +0 -0
  154. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/tests/test_scheduler.py +0 -0
  155. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/tests/test_server.py +0 -0
  156. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/tests/test_token_counter.py +0 -0
  157. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/tests/test_tui.py +0 -0
  158. {codex_agent_framework-0.1.61 → codex_agent_framework-0.1.63}/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.61
3
+ Version: 0.1.63
4
4
  Summary: A lightweight event-driven Codex agent runtime.
5
5
  Author: Baptiste
6
6
  License-Expression: MIT
@@ -427,7 +427,7 @@ class ProcessAgentRuntime(AgentRuntime):
427
427
  return None
428
428
  try:
429
429
  return message_from_data(message)
430
- except Exception:
430
+ except (TypeError, ValueError):
431
431
  return modict(message)
432
432
 
433
433
  def is_cached_wrapper(self, message):
@@ -1,7 +1,8 @@
1
- import json
2
1
  import sys
3
2
  import threading
4
3
 
4
+ from codex_agent.config import parse_config_value
5
+
5
6
  from .output import dump_ndjson, output_payload, key_value_text
6
7
  from .runner import in_process_runner, process_runner, server_runner
7
8
 
@@ -71,21 +72,6 @@ def run_tools(args, extra_args=None):
71
72
  return payload
72
73
 
73
74
 
74
- def parse_config_value(value):
75
- text = str(value)
76
- lowered = text.lower()
77
- if lowered == "true":
78
- return True
79
- if lowered == "false":
80
- return False
81
- if lowered == "null" or lowered == "none":
82
- return None
83
- try:
84
- return json.loads(text)
85
- except json.JSONDecodeError:
86
- return value
87
-
88
-
89
75
  def parse_config_set_args(extra_args):
90
76
  args = list(extra_args or [])
91
77
  if not args:
@@ -33,6 +33,7 @@ def command(func=None, **options):
33
33
 
34
34
 
35
35
  def command_options(func):
36
+ """Return command decorator options from a function or bound method."""
36
37
  options = getattr(func, COMMAND_MARKER, None)
37
38
  if options is not None:
38
39
  return options
@@ -17,6 +17,7 @@ DEFAULT_COMPACTION_PROMPT = text_content(DEFAULT_COMPACTION_PROMPT_PATH)
17
17
 
18
18
 
19
19
  def parse_config_value(value):
20
+ """Parse CLI/config scalar strings into bool, null, numeric, or JSON values."""
20
21
  if isinstance(value, bool):
21
22
  return value
22
23
  if not isinstance(value, str):
@@ -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
 
@@ -6,12 +6,6 @@ import re
6
6
  from typing import Union
7
7
  import json
8
8
 
9
- def log(*data):
10
- if False:
11
- print(*data)
12
- input()
13
-
14
-
15
9
  class END:
16
10
  #sentinel value for end of stream
17
11
  pass
@@ -376,7 +370,7 @@ class XMLExtractor(Extractor):
376
370
  try:
377
371
  # JSON-compatible values: numbers, booleans, null, dicts, lists…
378
372
  value = json.loads(raw)
379
- except Exception:
373
+ except ValueError:
380
374
  # else keep raw string
381
375
  value = raw
382
376
 
@@ -455,7 +449,7 @@ class MarkdownBlockExtractor(Extractor):
455
449
  raw_val = m.group("qvalue") or m.group("uvalue") or ""
456
450
  try:
457
451
  value = json.loads(raw_val)
458
- except Exception:
452
+ except ValueError:
459
453
  value = raw_val
460
454
  result[name] = value
461
455
 
@@ -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,36 +319,31 @@ def split_history_turns(messages):
318
319
  return turns, current
319
320
 
320
321
  def find_pattern(text, pattern=None):
321
- """
322
- Renvoie une liste de tuples contenant TOUS les groupes capturés par le pattern.
322
+ """Return captured regex groups for every match in ``text``.
323
323
 
324
- - Si le pattern contient 1 groupe -> tuple de 1 élément
325
- - Si le pattern contient n groupes -> tuple de n éléments
324
+ The default pattern extracts fenced Python code blocks. Each result is a
325
+ tuple containing all capture groups for one match, even when there is only
326
+ one group.
326
327
  """
327
-
328
328
  pattern = pattern or r'```python(.*?)```'
329
329
  iterator = re.finditer(pattern, text, re.DOTALL)
330
-
331
- return [
332
- tuple(match.groups()) # groups() renvoie *tous* les groupes capturés
333
- for match in iterator
334
- ]
330
+ return [tuple(match.groups()) for match in iterator]
335
331
 
336
332
  def format(string, context=None):
337
- # Si aucun contexte n'est fourni, utiliser un dictionnaire vide
338
- if context is None:
339
- context = {}
340
- # Trouver les expressions entre <<...>>
333
+ """Interpolate ``<<expr>>`` fragments with values evaluated from ``context``.
334
+
335
+ Expressions that fail to evaluate are left unchanged. This helper is meant
336
+ for trusted templates only because expressions are evaluated with ``eval``.
337
+ """
338
+ context = {} if context is None else context
339
+
341
340
  def replace_expr(match):
342
341
  expr = match.group(1)
343
342
  try:
344
- # Évaluer l'expression dans le contexte donné et la convertir en chaîne
345
343
  return str(eval(expr, context))
346
344
  except Exception:
347
- # print(f"could not evaluate expr: {expr}\n Exception:\n {str(e)}")
348
- # En cas d'erreur, retourner l'expression non évaluée
349
- return '<<' + expr + '>>'
350
- # Remplacer chaque expression par son évaluation
345
+ return f"<<{expr}>>"
346
+
351
347
  return re.sub(r'<<(.*?)>>', replace_expr, string)
352
348
 
353
349
  def timestamp(digits=3):
@@ -0,0 +1 @@
1
+ __version__ = "0.1.63"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codex-agent-framework
3
- Version: 0.1.61
3
+ Version: 0.1.63
4
4
  Summary: A lightweight event-driven Codex agent runtime.
5
5
  Author: Baptiste
6
6
  License-Expression: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "codex-agent-framework"
7
- version = "0.1.61"
7
+ version = "0.1.63"
8
8
  description = "A lightweight event-driven Codex agent runtime."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -3,6 +3,7 @@ from types import SimpleNamespace
3
3
  from modict import modict
4
4
 
5
5
  from codex_agent.agent_runtime import AgentRuntime, ProcessAgentRuntime
6
+ from codex_agent.message import UserMessage
6
7
 
7
8
 
8
9
  class FakeRuntime(AgentRuntime):
@@ -119,6 +120,24 @@ def test_runtime_submit_prompt_routes_text_to_submit_message():
119
120
  assert runtime.messages[-1].content == "hello"
120
121
 
121
122
 
123
+ def test_process_runtime_cached_message_from_event_rehydrates_known_messages():
124
+ runtime = ProcessAgentRuntime()
125
+
126
+ message = runtime.cached_message_from_event({"type": "user_message", "content": "hello"})
127
+
128
+ assert isinstance(message, UserMessage)
129
+ assert message.content == "hello"
130
+
131
+
132
+ def test_process_runtime_cached_message_from_event_keeps_unknown_payloads_as_modict():
133
+ runtime = ProcessAgentRuntime()
134
+
135
+ message = runtime.cached_message_from_event({"type": "future_message", "content": "hello"})
136
+
137
+ assert isinstance(message, modict)
138
+ assert message == {"type": "future_message", "content": "hello"}
139
+
140
+
122
141
  def test_process_runtime_update_config_preserves_namespace_cache_boundary(monkeypatch):
123
142
  runtime = ProcessAgentRuntime()
124
143
  runtime.cache["config"] = modict(model="cached")
@@ -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
 
@@ -1,4 +1,6 @@
1
1
  import importlib
2
+ import json
3
+
2
4
  cli_main = importlib.import_module("codex_agent.cli.main")
3
5
  headless = importlib.import_module("codex_agent.cli.headless")
4
6
  runner = importlib.import_module("codex_agent.cli.runner")
@@ -99,6 +101,30 @@ def test_config_get_key(monkeypatch, capsys):
99
101
  assert capsys.readouterr().out.strip() == "test-model"
100
102
 
101
103
 
104
+ def test_config_set_key_value_parses_central_config_scalars(monkeypatch, capsys):
105
+ patch_client(monkeypatch)
106
+
107
+ cli_main.main([
108
+ "config",
109
+ "set",
110
+ "enabled",
111
+ "yes",
112
+ "limit",
113
+ "128000",
114
+ "disabled",
115
+ "off",
116
+ "plugins",
117
+ '["memory"]',
118
+ "--json",
119
+ ])
120
+
121
+ payload = json.loads(capsys.readouterr().out)
122
+ assert payload["enabled"] is True
123
+ assert payload["limit"] == 128000
124
+ assert payload["disabled"] is False
125
+ assert payload["plugins"] == ["memory"]
126
+
127
+
102
128
  def test_config_set_key_value(monkeypatch, capsys):
103
129
  patch_client(monkeypatch)
104
130
 
@@ -1,6 +1,10 @@
1
1
  from codex_agent import service
2
2
 
3
3
 
4
+ def test_service_name_alias_points_to_server_service_for_compatibility():
5
+ assert service.SERVICE_NAME == service.SERVER_SERVICE_NAME
6
+
7
+
4
8
  def test_install_user_service_writes_server_and_tray_units(tmp_path, monkeypatch):
5
9
  calls = []
6
10
  monkeypatch.setenv("HOME", str(tmp_path))
@@ -6,7 +6,7 @@ from PIL import Image as PILImage
6
6
  from codex_agent.message import AssistantMessage, CompactionMessage, DeveloperMessage, InterruptMessage, ServerToolResponseMessage, ToolResultMessage, UserMessage
7
7
  from codex_agent import utils
8
8
  from codex_agent.image import ImageMessage, image_input_token_count
9
- from codex_agent.stream_utils import MappingStreamProcessor, MappingStreamSplitter, Splitter, Tee, fenced
9
+ from codex_agent.stream_utils import MarkdownBlockExtractor, MappingStreamProcessor, MappingStreamSplitter, Splitter, Tee, XMLExtractor, fenced
10
10
  from codex_agent.utils import split_history_turns
11
11
  from codex_agent.utils import project_python_env
12
12
 
@@ -43,6 +43,26 @@ def test_pack_msgs_formats_named_messages():
43
43
  assert utils.pack_msgs(messages) == "user:\nhello\n\nassistant:\ndone\n\n"
44
44
 
45
45
 
46
+ def test_find_pattern_returns_capture_groups_for_default_python_fences():
47
+ text = "before\n```python\nprint('hello')\n```\nafter"
48
+
49
+ assert utils.find_pattern(text) == [("\nprint('hello')\n",)]
50
+
51
+
52
+ def test_find_pattern_returns_all_capture_groups_for_custom_pattern():
53
+ text = "ticket ABC-123 and ticket XYZ-999"
54
+
55
+ assert utils.find_pattern(text, r"ticket ([A-Z]+)-(\d+)") == [("ABC", "123"), ("XYZ", "999")]
56
+
57
+
58
+ def test_format_interpolates_trusted_expressions_from_context():
59
+ assert utils.format("hello <<name.upper()>> #<<count + 1>>", {"name": "pandora", "count": 2}) == "hello PANDORA #3"
60
+
61
+
62
+ def test_format_keeps_expression_placeholder_when_evaluation_fails():
63
+ assert utils.format("hello <<missing + 1>>") == "hello <<missing + 1>>"
64
+
65
+
46
66
  def test_logger_creates_parent_directory_and_truncates_file(tmp_path):
47
67
  log_file = tmp_path / "logs" / "log.txt"
48
68
 
@@ -83,6 +103,18 @@ def test_mapping_stream_splitter_can_be_reused():
83
103
  assert list(second["text"]) == ["b"]
84
104
 
85
105
 
106
+ def test_xml_extractor_parse_attrs_preserves_invalid_json_values():
107
+ attrs = XMLExtractor.parse_attrs('count=3 raw={oops label="hello" enabled=true')
108
+
109
+ assert attrs == {"count": 3, "raw": "{oops", "label": "hello", "enabled": True}
110
+
111
+
112
+ def test_markdown_block_extractor_parse_kwargs_preserves_invalid_json_values():
113
+ kwargs = MarkdownBlockExtractor.parse_kwargs('count=3, raw={oops, label="hello", enabled=true')
114
+
115
+ assert kwargs == {"count": 3, "raw": "{oops", "label": "hello", "enabled": True}
116
+
117
+
86
118
  def test_mapping_stream_processor_is_direct_passthrough_without_processors():
87
119
  source = iter([{"content": "a"}, {"content": "b"}])
88
120
  processor = MappingStreamProcessor(processors={"content": []}, threaded=True)
@@ -1 +0,0 @@
1
- __version__ = "0.1.61"