codex-agent-framework 0.1.53__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.53 → codex_agent_framework-0.1.55}/CHANGELOG.md +20 -1
- {codex_agent_framework-0.1.53/codex_agent_framework.egg-info → codex_agent_framework-0.1.55}/PKG-INFO +3 -1
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/README.md +2 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/agent.py +0 -1
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/agent_runtime.py +16 -6
- codex_agent_framework-0.1.55/codex_agent/builtin_commands.py +196 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/client.py +39 -7
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/config.py +154 -67
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/mainloop.py +4 -2
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/server/app.py +2 -1
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/server/core.py +5 -1
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/sessions.py +37 -4
- {codex_agent_framework-0.1.53 → 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.53 → codex_agent_framework-0.1.55/codex_agent_framework.egg-info}/PKG-INFO +3 -1
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/pyproject.toml +1 -1
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/tests/test_agent_audio_hooks.py +7 -7
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/tests/test_agent_commands.py +56 -13
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/tests/test_agent_desktop.py +2 -2
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/tests/test_agent_environment.py +3 -3
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/tests/test_agent_observe.py +1 -1
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/tests/test_agent_plugins.py +1 -1
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/tests/test_agent_providers_config.py +23 -7
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/tests/test_agent_runtime.py +24 -2
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/tests/test_agent_tools.py +9 -7
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/tests/test_chat.py +23 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/tests/test_cli_headless.py +2 -1
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/tests/test_client.py +77 -4
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/tests/test_hooks.py +1 -1
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/tests/test_server.py +38 -3
- codex_agent_framework-0.1.53/codex_agent/builtin_commands.py +0 -152
- codex_agent_framework-0.1.53/codex_agent/version.py +0 -1
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/LICENSE +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/MANIFEST.in +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/__init__.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/__main__.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/ai.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/browser.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/__init__.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/bash/__init__.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/bash/requirements.txt +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/browser/__init__.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/browser/requirements.txt +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/content/__init__.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/content/requirements.txt +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/content/system.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/content/vision.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/context/__init__.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/desktop/__init__.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/desktop/requirements.txt +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/environment/__init__.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/environment/requirements.txt +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/files/__init__.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/files/requirements.txt +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/files/tools.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/image_generation/__init__.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/image_generation/image_generation_system_prompt.txt +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/image_generation/requirements.txt +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/interface/__init__.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/interface/requirements.txt +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/memory/__init__.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/memory/requirements.txt +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/planner/__init__.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/planner/requirements.txt +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/python/__init__.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/python/requirements.txt +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/realtime/__init__.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/realtime/audio.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/realtime/bridge.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/realtime/manager.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/realtime/requirements.txt +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/realtime/session.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/scheduler/__init__.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/scheduler/requirements.txt +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/subagents/__init__.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/subagents/profiles/explorer.json +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/subagents/requirements.txt +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/tts/__init__.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/tts/requirements.txt +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/builtin_plugins/web_search/__init__.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/cli/__init__.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/cli/headless.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/cli/main.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/cli/output.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/cli/root.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/cli/runner.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/command.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/context.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/desktop.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/event.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/get_text/__init__.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/get_text/default_gitignore +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/get_text/get_text.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/get_text/simpler_get_text.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/hooks.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/image.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/latex.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/message.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/plugin.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/prompts/system_prompt.txt +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/provider.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/runtime.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/scheduler.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/scripts/install-system-dependencies.sh +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/server/__init__.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/service.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/status.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/stream.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/stream_utils.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/tool.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/tray.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/tui/__init__.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/tui/app.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/tui/chat.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/tui/lifecycle.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/tui/log.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/tui/status_bar.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/tui/style.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/tui/transcript.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/utils.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent/worker.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent_framework.egg-info/SOURCES.txt +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent_framework.egg-info/dependency_links.txt +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent_framework.egg-info/entry_points.txt +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent_framework.egg-info/requires.txt +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/codex_agent_framework.egg-info/top_level.txt +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/dependencies.txt +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/setup.cfg +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/tests/test_agent.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/tests/test_agent_context.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/tests/test_agent_image_generation.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/tests/test_agent_realtime.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/tests/test_agent_response.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/tests/test_agent_sessions.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/tests/test_agent_subagents.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/tests/test_agent_turns.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/tests/test_ai.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/tests/test_browser.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/tests/test_builtin_config.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/tests/test_cli.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/tests/test_cli_root.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/tests/test_events.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/tests/test_get_text_browser.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/tests/test_image_message.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/tests/test_local_desktop.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/tests/test_memory.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/tests/test_messages.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/tests/test_planner.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/tests/test_scheduler.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/tests/test_service.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/tests/test_tui.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/tests/test_utils.py +0 -0
- {codex_agent_framework-0.1.53 → codex_agent_framework-0.1.55}/tests/test_worker.py +0 -0
|
@@ -4,10 +4,29 @@ 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
|
+
|
|
15
|
+
## [0.1.54] - 2026-05-17
|
|
16
|
+
### Changed
|
|
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`.
|
|
18
|
+
- Keep config persistence explicit for SDK-created agents while still materializing `agent.config.json` for long-lived server sessions at server bootstrap.
|
|
19
|
+
- Simplify config loading by relying on direct `Config.load(...)` with ignored unknown extras instead of carrying legacy config shims.
|
|
20
|
+
- Hide empty plugin config namespaces from `/config` output so only agent config and plugins with actual exposed config values are shown.
|
|
21
|
+
- Align runtime/server/client paths and tests with the namespaced config model and the minimal default plugin surface.
|
|
22
|
+
|
|
23
|
+
### Tests
|
|
24
|
+
- Validate with targeted config/server/runtime coverage and the full suite at 611 passed.
|
|
25
|
+
|
|
7
26
|
## [0.1.53] - 2026-05-16
|
|
8
27
|
### Changed
|
|
9
28
|
- Default new agents to a smaller built-in plugin surface: `files`, `environment`, `memory`, `planner`, `scheduler`, and `context`.
|
|
10
|
-
-
|
|
29
|
+
- Persist `~/.codex-agent/agent.config.json` only on explicit save/update actions instead of automatically on agent initialization.
|
|
11
30
|
- Make `web_search` and `image_generation` plugin-first server tools: loading the plugin now controls whether the corresponding Responses API tool is exposed.
|
|
12
31
|
- Remove the redundant `web_search_enabled` and `image_generation_enabled` config flags from the public agent config surface.
|
|
13
32
|
- Update docs and tests to reflect the minimal default plugin set and plugin-driven server tool activation.
|
|
@@ -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
|
|
@@ -54,7 +54,6 @@ class Agent:
|
|
|
54
54
|
self.config_manager.update(kwargs, save=False)
|
|
55
55
|
self.init_working_directories(root_dir=explicit_root_dir or launch_cwd, cwd=explicit_cwd)
|
|
56
56
|
self.config_manager.apply_openai_capability_defaults()
|
|
57
|
-
self.config_manager.save()
|
|
58
57
|
self.events = EventBus()
|
|
59
58
|
self.tools_manager = ToolsManager(self)
|
|
60
59
|
self.providers_manager = ProvidersManager(self)
|
|
@@ -189,8 +189,8 @@ class InProcessAgentRuntime(AgentRuntime):
|
|
|
189
189
|
def config(self):
|
|
190
190
|
return self.agent.config
|
|
191
191
|
|
|
192
|
-
def update_config(self, values=None, save=True, **kwargs):
|
|
193
|
-
return self.agent.config_manager.
|
|
192
|
+
def update_config(self, values=None, save=True, namespace="agent", **kwargs):
|
|
193
|
+
return self.agent.config_manager.update_config(namespace, values, save=save, **kwargs)
|
|
194
194
|
|
|
195
195
|
def session(self):
|
|
196
196
|
return self.agent.session
|
|
@@ -588,11 +588,16 @@ class ProcessAgentRuntime(AgentRuntime):
|
|
|
588
588
|
def config(self):
|
|
589
589
|
return self._cached_call("config")
|
|
590
590
|
|
|
591
|
-
def update_config(self, values=None, save=True, **kwargs):
|
|
591
|
+
def update_config(self, values=None, save=True, namespace="agent", **kwargs):
|
|
592
592
|
payload = dict(values or {})
|
|
593
593
|
payload.update(kwargs)
|
|
594
|
-
result = self.call(
|
|
595
|
-
|
|
594
|
+
result = self.call(
|
|
595
|
+
"update_config",
|
|
596
|
+
{"namespace": namespace, "values": payload, "save": save},
|
|
597
|
+
timeout=self.command_timeout,
|
|
598
|
+
)
|
|
599
|
+
if namespace == "agent":
|
|
600
|
+
self.cache["config"] = result
|
|
596
601
|
return result
|
|
597
602
|
|
|
598
603
|
def session(self):
|
|
@@ -703,6 +708,7 @@ class _RuntimeFuture:
|
|
|
703
708
|
|
|
704
709
|
def agent_worker_main(command_queue, response_queue, event_queue, agent_kwargs):
|
|
705
710
|
agent = Agent(**dict(agent_kwargs or {}))
|
|
711
|
+
agent.config_manager.save()
|
|
706
712
|
|
|
707
713
|
def emit_event(event):
|
|
708
714
|
event_queue.put(event_payload(event))
|
|
@@ -749,7 +755,11 @@ def run_worker_command(agent, name, payload):
|
|
|
749
755
|
if name == "config":
|
|
750
756
|
return agent.config
|
|
751
757
|
if name == "update_config":
|
|
752
|
-
return agent.config_manager.
|
|
758
|
+
return agent.config_manager.update_config(
|
|
759
|
+
payload.get("namespace", "agent"),
|
|
760
|
+
payload.get("values") or {},
|
|
761
|
+
save=payload.get("save", True),
|
|
762
|
+
)
|
|
753
763
|
if name == "session":
|
|
754
764
|
return agent.session
|
|
755
765
|
if name == "sessions":
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
"""Built-in slash commands."""
|
|
2
|
+
|
|
3
|
+
from modict import modict
|
|
4
|
+
|
|
5
|
+
from .command import command
|
|
6
|
+
from .config import parse_config_value
|
|
7
|
+
from .sessions import AgentSession
|
|
8
|
+
from .tool import get_agent
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _format_lines(value, prefix=""):
|
|
12
|
+
if isinstance(value, dict):
|
|
13
|
+
lines = []
|
|
14
|
+
for key, child in value.items():
|
|
15
|
+
full_key = f"{prefix}.{key}" if prefix else str(key)
|
|
16
|
+
if isinstance(child, dict):
|
|
17
|
+
lines.append(f"{full_key}:")
|
|
18
|
+
lines.extend(_format_lines(child, full_key))
|
|
19
|
+
else:
|
|
20
|
+
lines.append(f"{full_key}: {child!r}")
|
|
21
|
+
return lines
|
|
22
|
+
return [f"{prefix}: {value!r}" if prefix else repr(value)]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def format_config_output(value, prefix=""):
|
|
26
|
+
return "\n".join(_format_lines(value, prefix=prefix)) if isinstance(value, dict) else repr(value)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@command
|
|
30
|
+
def help():
|
|
31
|
+
agent = get_agent()
|
|
32
|
+
names = ", ".join(f"/{name}" for name in sorted(agent.commands))
|
|
33
|
+
return f"Available commands: {names}"
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@command
|
|
37
|
+
def compact(max_compacted_turns=None):
|
|
38
|
+
agent = get_agent()
|
|
39
|
+
max_compacted_turns = None if max_compacted_turns in (None, "", "all") else int(max_compacted_turns)
|
|
40
|
+
message = agent.compact_session(
|
|
41
|
+
model=agent.config.model,
|
|
42
|
+
instructions=agent.config_manager.system_message().content,
|
|
43
|
+
max_compacted_turns=max_compacted_turns,
|
|
44
|
+
)
|
|
45
|
+
if message is None:
|
|
46
|
+
return "Nothing to compact."
|
|
47
|
+
if message.get("status") == "failed":
|
|
48
|
+
return f"Compaction failed: {message.error}"
|
|
49
|
+
suffix = "" if max_compacted_turns is None else f" from {max_compacted_turns} compacted turn(s)"
|
|
50
|
+
return f"Compacted {message.compacted_message_count} messages{suffix}."
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _message_count_text(count):
|
|
54
|
+
return f"{count} message" if count == 1 else f"{count} messages"
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _session_infos(agent):
|
|
58
|
+
infos = []
|
|
59
|
+
for index, info in enumerate(AgentSession.list_session_infos(), start=1):
|
|
60
|
+
infos.append(modict(
|
|
61
|
+
index=index,
|
|
62
|
+
session_id=info.session_id,
|
|
63
|
+
title=info.title or info.session_id,
|
|
64
|
+
message_count=info.message_count,
|
|
65
|
+
current=info.session_id == agent.current_session_id,
|
|
66
|
+
))
|
|
67
|
+
return infos
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _format_session_list(agent):
|
|
71
|
+
infos = _session_infos(agent)
|
|
72
|
+
if not infos:
|
|
73
|
+
return "No saved sessions."
|
|
74
|
+
lines = ["Sessions:"]
|
|
75
|
+
for info in infos:
|
|
76
|
+
marker = "*" if info.current else " "
|
|
77
|
+
lines.append(
|
|
78
|
+
f"{marker} [{info.index}] {info.title} "
|
|
79
|
+
f"({info.session_id}, {_message_count_text(info.message_count)})"
|
|
80
|
+
)
|
|
81
|
+
return "\n".join(lines)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
@command
|
|
85
|
+
def session(action=None, *parts):
|
|
86
|
+
agent = get_agent()
|
|
87
|
+
if action is None:
|
|
88
|
+
return _format_session_list(agent)
|
|
89
|
+
|
|
90
|
+
action_text = str(action).strip()
|
|
91
|
+
action_lower = action_text.lower()
|
|
92
|
+
if action_lower == "new":
|
|
93
|
+
created = agent.start_new_session()
|
|
94
|
+
return f"Created session: {created.session_id}"
|
|
95
|
+
|
|
96
|
+
if action_lower == "rename":
|
|
97
|
+
title = " ".join(str(part) for part in parts).strip()
|
|
98
|
+
if not title:
|
|
99
|
+
raise ValueError("/session rename requires a title")
|
|
100
|
+
if agent.session is None:
|
|
101
|
+
raise ValueError("No active session to rename")
|
|
102
|
+
agent.session.title = title
|
|
103
|
+
agent.save_session()
|
|
104
|
+
return f"Renamed session: {title}"
|
|
105
|
+
|
|
106
|
+
if parts:
|
|
107
|
+
raise ValueError("Usage: /session, /session <index>, /session new, /session rename <title>")
|
|
108
|
+
|
|
109
|
+
try:
|
|
110
|
+
index = int(action_text)
|
|
111
|
+
except ValueError as exc:
|
|
112
|
+
raise ValueError(f"Unknown /session action: {action_text}") from exc
|
|
113
|
+
|
|
114
|
+
infos = _session_infos(agent)
|
|
115
|
+
if index < 1 or index > len(infos):
|
|
116
|
+
raise ValueError(f"Session index out of range: {index}")
|
|
117
|
+
loaded = agent.load_session(infos[index - 1].session_id)
|
|
118
|
+
return f"Loaded session: {loaded.session_id}"
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
@command
|
|
122
|
+
def config(path=None, value=None, *extra, **values):
|
|
123
|
+
agent = get_agent()
|
|
124
|
+
manager = agent.config_manager
|
|
125
|
+
|
|
126
|
+
if values:
|
|
127
|
+
unknown = sorted(key for key in values if key not in agent.config)
|
|
128
|
+
if unknown:
|
|
129
|
+
raise ValueError(f"Unknown model config keys: {', '.join(unknown)}")
|
|
130
|
+
parsed = {key: parse_config_value(raw) for key, raw in values.items()}
|
|
131
|
+
manager.update_config("agent", config=parsed, save=True)
|
|
132
|
+
changed = ", ".join(f"{key}={agent.config.get(key)!r}" for key in parsed)
|
|
133
|
+
return f"Updated config: {changed}" if changed else format_config_output(manager.export_tree())
|
|
134
|
+
|
|
135
|
+
if path is None:
|
|
136
|
+
if value is not None or extra:
|
|
137
|
+
raise ValueError("/config with no path does not accept extra arguments")
|
|
138
|
+
return format_config_output(manager.export_tree())
|
|
139
|
+
|
|
140
|
+
if path == "save":
|
|
141
|
+
manager.save_all()
|
|
142
|
+
return "Saved all config namespaces."
|
|
143
|
+
|
|
144
|
+
if value is None and not extra:
|
|
145
|
+
displayed = manager.get_path(path)
|
|
146
|
+
return format_config_output(displayed, prefix=path)
|
|
147
|
+
|
|
148
|
+
if value == "save" and not extra:
|
|
149
|
+
namespace, remainder = manager.namespace_path(path)
|
|
150
|
+
if remainder:
|
|
151
|
+
raise ValueError("Save targets a config namespace, not a nested key")
|
|
152
|
+
manager.save_config(namespace)
|
|
153
|
+
return f"Saved config namespace: {namespace}"
|
|
154
|
+
|
|
155
|
+
raw_value = " ".join(str(part) for part in ((value,) + extra))
|
|
156
|
+
updated = manager.set_path(path, raw_value, save=False)
|
|
157
|
+
return f"Updated config: {path}={updated!r}"
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
@command
|
|
161
|
+
def model(value=None):
|
|
162
|
+
if value is None:
|
|
163
|
+
return f"model: {get_agent().config.model!r}"
|
|
164
|
+
get_agent().config_manager.set_path("agent.model", value, save=True)
|
|
165
|
+
return f"Updated config: model={get_agent().config.model!r}"
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
@command
|
|
169
|
+
def reasoning(value=None):
|
|
170
|
+
agent = get_agent()
|
|
171
|
+
if value is None:
|
|
172
|
+
return f"reasoning: {agent.config.get('reasoning')!r}"
|
|
173
|
+
current = dict(agent.config.get("reasoning") or {})
|
|
174
|
+
current["effort"] = value
|
|
175
|
+
current.setdefault("summary", "auto")
|
|
176
|
+
agent.config_manager.update_config("agent", reasoning=current, save=True)
|
|
177
|
+
return f"Updated config: reasoning={agent.config.get('reasoning')!r}"
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
@command
|
|
181
|
+
def verbosity(value=None):
|
|
182
|
+
agent = get_agent()
|
|
183
|
+
if value is None:
|
|
184
|
+
return f"text: {agent.config.get('text')!r}"
|
|
185
|
+
current = dict(agent.config.get("text") or {})
|
|
186
|
+
current["verbosity"] = value
|
|
187
|
+
agent.config_manager.update_config("agent", text=current, save=True)
|
|
188
|
+
return f"Updated config: text={agent.config.get('text')!r}"
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
@command
|
|
192
|
+
def voice(value=None):
|
|
193
|
+
agent = get_agent()
|
|
194
|
+
if "tts" not in agent.plugins:
|
|
195
|
+
agent.load_builtin_plugin("tts")
|
|
196
|
+
return agent.plugins.tts.voice(value)
|
|
@@ -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):
|
|
@@ -327,10 +359,10 @@ class AgentClient:
|
|
|
327
359
|
def get_config(self):
|
|
328
360
|
return self.get("/config")
|
|
329
361
|
|
|
330
|
-
def update_config(self, values=None, save=True, **kwargs):
|
|
362
|
+
def update_config(self, values=None, save=True, namespace="agent", **kwargs):
|
|
331
363
|
payload = dict(values or {})
|
|
332
364
|
payload.update(kwargs)
|
|
333
|
-
return self.request("PATCH", "/config", json={"values": payload, "save": save})
|
|
365
|
+
return self.request("PATCH", "/config", json={"values": payload, "save": save, "namespace": namespace})
|
|
334
366
|
|
|
335
367
|
def tools(self):
|
|
336
368
|
return self.get("/tools")
|
|
@@ -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",
|