codex-agent-framework 0.1.54__tar.gz → 0.1.55__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.54 → codex_agent_framework-0.1.55}/CHANGELOG.md +8 -0
- {codex_agent_framework-0.1.54/codex_agent_framework.egg-info → codex_agent_framework-0.1.55}/PKG-INFO +3 -1
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/README.md +2 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/client.py +37 -5
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/tui/state.py +1 -4
- codex_agent_framework-0.1.55/codex_agent/version.py +1 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55/codex_agent_framework.egg-info}/PKG-INFO +3 -1
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/pyproject.toml +1 -1
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/tests/test_chat.py +23 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/tests/test_client.py +77 -4
- codex_agent_framework-0.1.54/codex_agent/version.py +0 -1
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/LICENSE +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/MANIFEST.in +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/__init__.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/__main__.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/agent.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/agent_runtime.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/ai.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/browser.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/builtin_commands.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/__init__.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/bash/__init__.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/bash/requirements.txt +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/browser/__init__.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/browser/requirements.txt +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/content/__init__.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/content/requirements.txt +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/content/system.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/content/vision.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/context/__init__.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/desktop/__init__.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/desktop/requirements.txt +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/environment/__init__.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/environment/requirements.txt +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/files/__init__.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/files/requirements.txt +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/files/tools.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/image_generation/__init__.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/image_generation/image_generation_system_prompt.txt +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/image_generation/requirements.txt +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/interface/__init__.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/interface/requirements.txt +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/memory/__init__.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/memory/requirements.txt +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/planner/__init__.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/planner/requirements.txt +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/python/__init__.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/python/requirements.txt +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/realtime/__init__.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/realtime/audio.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/realtime/bridge.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/realtime/manager.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/realtime/requirements.txt +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/realtime/session.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/scheduler/__init__.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/scheduler/requirements.txt +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/subagents/__init__.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/subagents/profiles/explorer.json +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/subagents/requirements.txt +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/tts/__init__.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/tts/requirements.txt +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/web_search/__init__.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/cli/__init__.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/cli/headless.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/cli/main.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/cli/output.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/cli/root.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/cli/runner.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/command.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/config.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/context.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/desktop.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/event.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/get_text/__init__.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/get_text/default_gitignore +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/get_text/get_text.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/get_text/simpler_get_text.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/hooks.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/image.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/latex.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/mainloop.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/message.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/plugin.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/prompts/system_prompt.txt +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/provider.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/runtime.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/scheduler.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/scripts/install-system-dependencies.sh +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/server/__init__.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/server/app.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/server/core.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/service.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/sessions.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/status.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/stream.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/stream_utils.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/tool.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/tray.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/tui/__init__.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/tui/app.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/tui/chat.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/tui/lifecycle.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/tui/log.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/tui/status_bar.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/tui/style.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/tui/transcript.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/utils.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/worker.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent_framework.egg-info/SOURCES.txt +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent_framework.egg-info/dependency_links.txt +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent_framework.egg-info/entry_points.txt +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent_framework.egg-info/requires.txt +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent_framework.egg-info/top_level.txt +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/dependencies.txt +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/setup.cfg +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/tests/test_agent.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/tests/test_agent_audio_hooks.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/tests/test_agent_commands.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/tests/test_agent_context.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/tests/test_agent_desktop.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/tests/test_agent_environment.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/tests/test_agent_image_generation.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/tests/test_agent_observe.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/tests/test_agent_plugins.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/tests/test_agent_providers_config.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/tests/test_agent_realtime.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/tests/test_agent_response.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/tests/test_agent_runtime.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/tests/test_agent_sessions.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/tests/test_agent_subagents.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/tests/test_agent_tools.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/tests/test_agent_turns.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/tests/test_ai.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/tests/test_browser.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/tests/test_builtin_config.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/tests/test_cli.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/tests/test_cli_headless.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/tests/test_cli_root.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/tests/test_events.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/tests/test_get_text_browser.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/tests/test_hooks.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/tests/test_image_message.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/tests/test_local_desktop.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/tests/test_memory.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/tests/test_messages.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/tests/test_planner.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/tests/test_scheduler.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/tests/test_server.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/tests/test_service.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/tests/test_tui.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/tests/test_utils.py +0 -0
- {codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/tests/test_worker.py +0 -0
|
@@ -4,6 +4,14 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
This project loosely follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and uses semantic versioning where practical.
|
|
6
6
|
|
|
7
|
+
## [0.1.55] - 2026-05-17
|
|
8
|
+
### Fixed
|
|
9
|
+
- Decouple SSE event-stream consumption from local client/TUI event handling so slow or blocked UI work no longer stalls event intake during long-running or heavy tool activity.
|
|
10
|
+
- Remove the unsafe TUI bridge fallback that could execute UI callbacks off the Textual thread after `call_from_thread(...)` failures.
|
|
11
|
+
|
|
12
|
+
### Tests
|
|
13
|
+
- Add regression coverage for slow local handlers and TUI bridge thread-safety, then validate with targeted client/chat tests (88 passed).
|
|
14
|
+
|
|
7
15
|
## [0.1.54] - 2026-05-17
|
|
8
16
|
### Changed
|
|
9
17
|
- Add a generic namespaced `/config` command surface so agent and plugin configs can be read, updated, and saved through a uniform dot-notation interface such as `/config agent.input_token_limit 200000`, `/config tts.voice nova`, `/config agent save`, and `/config save`.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codex-agent-framework
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.55
|
|
4
4
|
Summary: A lightweight event-driven Codex agent runtime.
|
|
5
5
|
Author: Baptiste
|
|
6
6
|
License-Expression: MIT
|
|
@@ -129,6 +129,8 @@ turn = stream.turn
|
|
|
129
129
|
print("\ncompleted:", turn.completed)
|
|
130
130
|
```
|
|
131
131
|
|
|
132
|
+
The built-in TUI uses the same streamed event model and is designed to stay responsive during heavier tool activity by decoupling event intake from UI rendering.
|
|
133
|
+
|
|
132
134
|
Add a small tool:
|
|
133
135
|
|
|
134
136
|
```python
|
|
@@ -80,6 +80,8 @@ turn = stream.turn
|
|
|
80
80
|
print("\ncompleted:", turn.completed)
|
|
81
81
|
```
|
|
82
82
|
|
|
83
|
+
The built-in TUI uses the same streamed event model and is designed to stay responsive during heavier tool activity by decoupling event intake from UI rendering.
|
|
84
|
+
|
|
83
85
|
Add a small tool:
|
|
84
86
|
|
|
85
87
|
```python
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import os
|
|
3
|
+
import queue
|
|
3
4
|
import threading
|
|
4
5
|
import time
|
|
5
6
|
from urllib.parse import urlencode
|
|
@@ -22,7 +23,9 @@ class AgentClient:
|
|
|
22
23
|
self._event_handlers = {}
|
|
23
24
|
self._event_thread = None
|
|
24
25
|
self._health_thread = None
|
|
26
|
+
self._dispatch_thread = None
|
|
25
27
|
self._event_stop = threading.Event()
|
|
28
|
+
self._local_event_queue = queue.Queue()
|
|
26
29
|
self._server_connected = False
|
|
27
30
|
self._event_stream_connected = False
|
|
28
31
|
self._event_stream_response = None
|
|
@@ -77,10 +80,35 @@ class AgentClient:
|
|
|
77
80
|
self.emit_local({"type": "client_event_error", "error": str(exc), "event": event_type})
|
|
78
81
|
return event
|
|
79
82
|
|
|
83
|
+
def _start_dispatch_thread(self):
|
|
84
|
+
if self._dispatch_thread and self._dispatch_thread.is_alive():
|
|
85
|
+
return self
|
|
86
|
+
self._dispatch_thread = threading.Thread(target=self._dispatch_loop, name="agent-client-dispatch", daemon=True)
|
|
87
|
+
self._dispatch_thread.start()
|
|
88
|
+
return self
|
|
89
|
+
|
|
90
|
+
def _enqueue_local(self, event):
|
|
91
|
+
self._start_dispatch_thread()
|
|
92
|
+
self._local_event_queue.put(modict(event))
|
|
93
|
+
return event
|
|
94
|
+
|
|
95
|
+
def _dispatch_loop(self):
|
|
96
|
+
while True:
|
|
97
|
+
try:
|
|
98
|
+
event = self._local_event_queue.get(timeout=0.1)
|
|
99
|
+
except queue.Empty:
|
|
100
|
+
if self._event_stop.is_set():
|
|
101
|
+
break
|
|
102
|
+
continue
|
|
103
|
+
if event is None:
|
|
104
|
+
break
|
|
105
|
+
self.emit_local(event)
|
|
106
|
+
|
|
80
107
|
def start_events(self):
|
|
81
108
|
if self._event_thread and self._event_thread.is_alive():
|
|
82
109
|
return self
|
|
83
110
|
self._event_stop.clear()
|
|
111
|
+
self._start_dispatch_thread()
|
|
84
112
|
self._event_thread = threading.Thread(target=self._event_loop, name="agent-client-events", daemon=True)
|
|
85
113
|
self._health_thread = threading.Thread(target=self._health_loop, name="agent-client-health", daemon=True)
|
|
86
114
|
self._event_thread.start()
|
|
@@ -89,10 +117,13 @@ class AgentClient:
|
|
|
89
117
|
|
|
90
118
|
def stop_events(self, timeout=0.2):
|
|
91
119
|
self._event_stop.set()
|
|
120
|
+
self._local_event_queue.put(None)
|
|
92
121
|
if self._event_thread:
|
|
93
122
|
self._event_thread.join(timeout)
|
|
94
123
|
if self._health_thread:
|
|
95
124
|
self._health_thread.join(timeout)
|
|
125
|
+
if self._dispatch_thread:
|
|
126
|
+
self._dispatch_thread.join(timeout)
|
|
96
127
|
return self
|
|
97
128
|
|
|
98
129
|
def _set_server_connected(self):
|
|
@@ -100,7 +131,7 @@ class AgentClient:
|
|
|
100
131
|
if self._server_connected:
|
|
101
132
|
return False
|
|
102
133
|
self._server_connected = True
|
|
103
|
-
self.
|
|
134
|
+
self._enqueue_local({"type": "server_connected"})
|
|
104
135
|
return True
|
|
105
136
|
|
|
106
137
|
def _set_server_offline(self, error=""):
|
|
@@ -108,7 +139,7 @@ class AgentClient:
|
|
|
108
139
|
if not self._server_connected:
|
|
109
140
|
return False
|
|
110
141
|
self._server_connected = False
|
|
111
|
-
self.
|
|
142
|
+
self._enqueue_local({"type": "server_offline", "error": str(error or "")})
|
|
112
143
|
return True
|
|
113
144
|
|
|
114
145
|
def _set_event_stream_connected(self, connected):
|
|
@@ -180,6 +211,7 @@ class AgentClient:
|
|
|
180
211
|
self._event_stop.wait(self.health_interval)
|
|
181
212
|
|
|
182
213
|
def _event_loop(self):
|
|
214
|
+
self._start_dispatch_thread()
|
|
183
215
|
while not self._event_stop.is_set():
|
|
184
216
|
try:
|
|
185
217
|
with requests.get(
|
|
@@ -200,7 +232,7 @@ class AgentClient:
|
|
|
200
232
|
break
|
|
201
233
|
self._record_event_stream_activity()
|
|
202
234
|
self.remember_event_sequence(event)
|
|
203
|
-
self.
|
|
235
|
+
self._enqueue_local(event)
|
|
204
236
|
finally:
|
|
205
237
|
self._clear_event_stream_response(response)
|
|
206
238
|
if stream_ended and not self._event_stop.is_set():
|
|
@@ -214,7 +246,7 @@ class AgentClient:
|
|
|
214
246
|
reconnecting = self._consume_event_reconnect_requested()
|
|
215
247
|
if not (reconnecting or (self._server_connected and self.is_event_idle_timeout(exc))):
|
|
216
248
|
self._set_server_offline(str(exc))
|
|
217
|
-
self.
|
|
249
|
+
self._enqueue_local(self.event_error_payload(exc))
|
|
218
250
|
self._event_stop.wait(0.1)
|
|
219
251
|
|
|
220
252
|
def event_error_payload(self, exc):
|
|
@@ -368,7 +400,7 @@ class AgentClient:
|
|
|
368
400
|
return self.post("/interrupt", {"reason": reason})
|
|
369
401
|
except Exception as exc:
|
|
370
402
|
payload = modict(interrupted=False, interrupt_requested=False, error=str(exc))
|
|
371
|
-
self.
|
|
403
|
+
self._enqueue_local({
|
|
372
404
|
"type": "client_event_error",
|
|
373
405
|
"error": str(exc),
|
|
374
406
|
"source": "interrupt",
|
|
@@ -60,10 +60,7 @@ class TuiAppBridge:
|
|
|
60
60
|
if self.app is None:
|
|
61
61
|
return None
|
|
62
62
|
callback = getattr(self.app, method)
|
|
63
|
-
|
|
64
|
-
return self.app.call_from_thread(callback, *args)
|
|
65
|
-
except RuntimeError:
|
|
66
|
-
return callback(*args)
|
|
63
|
+
return self.app.call_from_thread(callback, *args)
|
|
67
64
|
|
|
68
65
|
def refresh_status(self):
|
|
69
66
|
return self.call("refresh_status", skip_during_replay=True)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.55"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codex-agent-framework
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.55
|
|
4
4
|
Summary: A lightweight event-driven Codex agent runtime.
|
|
5
5
|
Author: Baptiste
|
|
6
6
|
License-Expression: MIT
|
|
@@ -129,6 +129,8 @@ turn = stream.turn
|
|
|
129
129
|
print("\ncompleted:", turn.completed)
|
|
130
130
|
```
|
|
131
131
|
|
|
132
|
+
The built-in TUI uses the same streamed event model and is designed to stay responsive during heavier tool activity by decoupling event intake from UI rendering.
|
|
133
|
+
|
|
132
134
|
Add a small tool:
|
|
133
135
|
|
|
134
136
|
```python
|
|
@@ -1204,6 +1204,29 @@ def test_chat_live_deltas_render_before_tool_start(monkeypatch):
|
|
|
1204
1204
|
assert calls.index(("write_agent_delta", ("I will inspect.",))) < calls.index(("write_tool", ("bash", "call_1", "running")))
|
|
1205
1205
|
|
|
1206
1206
|
|
|
1207
|
+
def test_tui_app_bridge_does_not_fallback_to_off_thread_callback_execution():
|
|
1208
|
+
chat = Chat()
|
|
1209
|
+
calls = []
|
|
1210
|
+
|
|
1211
|
+
class App:
|
|
1212
|
+
def call_from_thread(self, callback, *args):
|
|
1213
|
+
raise RuntimeError("app not running")
|
|
1214
|
+
|
|
1215
|
+
def write_agent_delta(self, delta):
|
|
1216
|
+
calls.append(("delta", delta))
|
|
1217
|
+
|
|
1218
|
+
chat.app = App()
|
|
1219
|
+
|
|
1220
|
+
try:
|
|
1221
|
+
chat.view.write_agent_delta("hello")
|
|
1222
|
+
except RuntimeError as exc:
|
|
1223
|
+
assert "app not running" in str(exc)
|
|
1224
|
+
else:
|
|
1225
|
+
raise AssertionError("expected RuntimeError")
|
|
1226
|
+
|
|
1227
|
+
assert calls == []
|
|
1228
|
+
|
|
1229
|
+
|
|
1207
1230
|
def test_chat_flushes_replay_deltas_before_live_deltas(monkeypatch):
|
|
1208
1231
|
chat = Chat()
|
|
1209
1232
|
calls = []
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import json
|
|
2
|
+
import threading
|
|
3
|
+
import time
|
|
2
4
|
|
|
3
5
|
import requests
|
|
4
6
|
|
|
@@ -47,6 +49,72 @@ def test_agent_client_keeps_dispatching_when_handler_fails():
|
|
|
47
49
|
assert seen == ["boom"]
|
|
48
50
|
|
|
49
51
|
|
|
52
|
+
def test_agent_client_local_dispatch_queue_decouples_sse_from_slow_handler(monkeypatch):
|
|
53
|
+
client = AgentClient(timeout=0.01)
|
|
54
|
+
started = threading.Event()
|
|
55
|
+
release = threading.Event()
|
|
56
|
+
handled = []
|
|
57
|
+
request_count = {"count": 0}
|
|
58
|
+
|
|
59
|
+
def slow_handler(event):
|
|
60
|
+
handled.append(("start", event.type))
|
|
61
|
+
started.set()
|
|
62
|
+
release.wait(timeout=1.0)
|
|
63
|
+
handled.append(("done", event.type))
|
|
64
|
+
|
|
65
|
+
client.on("assistant_turn_start_event", slow_handler)
|
|
66
|
+
client.on("tool_call_start_event", lambda event: (handled.append(("fast", event.type)), client._event_stop.set()))
|
|
67
|
+
|
|
68
|
+
class Response:
|
|
69
|
+
content = b""
|
|
70
|
+
|
|
71
|
+
def __init__(self, lines):
|
|
72
|
+
self.lines = lines
|
|
73
|
+
|
|
74
|
+
def __enter__(self):
|
|
75
|
+
return self
|
|
76
|
+
|
|
77
|
+
def __exit__(self, *args):
|
|
78
|
+
return False
|
|
79
|
+
|
|
80
|
+
def raise_for_status(self):
|
|
81
|
+
pass
|
|
82
|
+
|
|
83
|
+
def iter_lines(self, chunk_size=None, decode_unicode=True):
|
|
84
|
+
for line in self.lines:
|
|
85
|
+
yield line
|
|
86
|
+
|
|
87
|
+
def fake_get(*args, **kwargs):
|
|
88
|
+
request_count["count"] += 1
|
|
89
|
+
if request_count["count"] == 1:
|
|
90
|
+
return Response([
|
|
91
|
+
'event: assistant_turn_start_event',
|
|
92
|
+
'data: {"type": "assistant_turn_start_event", "sequence": 1}',
|
|
93
|
+
'',
|
|
94
|
+
'event: tool_call_start_event',
|
|
95
|
+
'data: {"type": "tool_call_start_event", "sequence": 2}',
|
|
96
|
+
'',
|
|
97
|
+
])
|
|
98
|
+
return Response([])
|
|
99
|
+
|
|
100
|
+
monkeypatch.setattr("codex_agent.client.requests.get", fake_get)
|
|
101
|
+
monkeypatch.setattr(client._event_stop, "wait", lambda delay: None)
|
|
102
|
+
|
|
103
|
+
thread = threading.Thread(target=client._event_loop, daemon=True)
|
|
104
|
+
thread.start()
|
|
105
|
+
|
|
106
|
+
assert started.wait(timeout=1.0) is True
|
|
107
|
+
assert client._last_event_sequence == 2
|
|
108
|
+
|
|
109
|
+
release.set()
|
|
110
|
+
thread.join(timeout=1.0)
|
|
111
|
+
client.stop_events(timeout=0.2)
|
|
112
|
+
|
|
113
|
+
assert ("start", "assistant_turn_start_event") in handled
|
|
114
|
+
assert ("done", "assistant_turn_start_event") in handled
|
|
115
|
+
assert ("fast", "tool_call_start_event") in handled
|
|
116
|
+
|
|
117
|
+
|
|
50
118
|
def test_agent_client_parses_bad_sse_json_as_client_error():
|
|
51
119
|
client = AgentClient()
|
|
52
120
|
lines = [
|
|
@@ -113,7 +181,8 @@ def test_agent_client_submit_prompt_posts_prompt(monkeypatch):
|
|
|
113
181
|
def test_agent_client_interrupt_is_best_effort_on_http_error(monkeypatch):
|
|
114
182
|
client = AgentClient(base_url="http://agent.local")
|
|
115
183
|
seen = []
|
|
116
|
-
|
|
184
|
+
received = threading.Event()
|
|
185
|
+
client.on("client_event_error", lambda event: (seen.append(event), received.set()))
|
|
117
186
|
|
|
118
187
|
def fake_request(method, url, **kwargs):
|
|
119
188
|
raise requests.ConnectionError("server disappeared")
|
|
@@ -125,7 +194,9 @@ def test_agent_client_interrupt_is_best_effort_on_http_error(monkeypatch):
|
|
|
125
194
|
assert result.interrupted is False
|
|
126
195
|
assert result.interrupt_requested is False
|
|
127
196
|
assert "server disappeared" in result.error
|
|
197
|
+
assert received.wait(timeout=1.0) is True
|
|
128
198
|
assert seen[-1].source == "interrupt"
|
|
199
|
+
client.stop_events(timeout=0.2)
|
|
129
200
|
|
|
130
201
|
|
|
131
202
|
def test_agent_client_fetches_replay_events_with_cursor_and_session(monkeypatch):
|
|
@@ -195,8 +266,9 @@ def test_agent_client_emits_offline_then_connected_on_reconnect(monkeypatch):
|
|
|
195
266
|
monkeypatch.setattr(client._event_stop, "wait", lambda delay: None)
|
|
196
267
|
|
|
197
268
|
client._event_loop()
|
|
269
|
+
client.stop_events(timeout=0.2)
|
|
198
270
|
|
|
199
|
-
assert events == ["server_connected", "server_ping", "server_offline", "server_connected", "server_ping"]
|
|
271
|
+
assert events[:5] == ["server_connected", "server_ping", "server_offline", "server_connected", "server_ping"]
|
|
200
272
|
|
|
201
273
|
|
|
202
274
|
def test_agent_client_does_not_emit_server_connected_for_failed_connection(monkeypatch):
|
|
@@ -347,12 +419,13 @@ def test_agent_client_reconnects_after_idle_read_timeout_without_offline_event(m
|
|
|
347
419
|
monkeypatch.setattr(client._event_stop, "wait", lambda delay: False)
|
|
348
420
|
|
|
349
421
|
client._event_loop()
|
|
422
|
+
client.stop_events(timeout=0.2)
|
|
350
423
|
|
|
351
|
-
assert urls == [
|
|
424
|
+
assert urls[:2] == [
|
|
352
425
|
"http://agent.local/events?client_pid=1234",
|
|
353
426
|
"http://agent.local/events?client_pid=1234&after_sequence=2",
|
|
354
427
|
]
|
|
355
|
-
assert events == ["server_connected", "tool_call_start_event", "tool_call_done_event"]
|
|
428
|
+
assert events[:3] == ["server_connected", "tool_call_start_event", "tool_call_done_event"]
|
|
356
429
|
|
|
357
430
|
|
|
358
431
|
def test_agent_client_health_loop_reconnects_stale_event_stream_without_offline(monkeypatch):
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.1.54"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/builtin_commands.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/get_text/__init__.py
RENAMED
|
File without changes
|
{codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/get_text/default_gitignore
RENAMED
|
File without changes
|
{codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/get_text/get_text.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/prompts/system_prompt.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/codex_agent/server/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/tests/test_agent_audio_hooks.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/tests/test_agent_environment.py
RENAMED
|
File without changes
|
{codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/tests/test_agent_image_generation.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/tests/test_agent_providers_config.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{codex_agent_framework-0.1.54 → codex_agent_framework-0.1.55}/tests/test_get_text_browser.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|