codex-agent-framework 0.1.23__tar.gz → 0.1.25__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 (136) hide show
  1. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/CHANGELOG.md +23 -0
  2. {codex_agent_framework-0.1.23/codex_agent_framework.egg-info → codex_agent_framework-0.1.25}/PKG-INFO +5 -3
  3. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/README.md +4 -2
  4. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/codex_agent/__init__.py +23 -3
  5. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/codex_agent/agent.py +65 -74
  6. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/codex_agent/ai.py +41 -2
  7. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/codex_agent/builtin_commands.py +7 -21
  8. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/codex_agent/builtin_plugins/bash/__init__.py +7 -0
  9. codex_agent_framework-0.1.25/codex_agent/builtin_plugins/bash/requirements.txt +1 -0
  10. codex_agent_framework-0.1.25/codex_agent/builtin_plugins/browser/requirements.txt +2 -0
  11. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/codex_agent/builtin_plugins/content/__init__.py +10 -0
  12. codex_agent_framework-0.1.25/codex_agent/builtin_plugins/content/requirements.txt +11 -0
  13. codex_agent_framework-0.1.25/codex_agent/builtin_plugins/desktop/requirements.txt +4 -0
  14. codex_agent_framework-0.1.25/codex_agent/builtin_plugins/environment/__init__.py +285 -0
  15. codex_agent_framework-0.1.25/codex_agent/builtin_plugins/environment/requirements.txt +1 -0
  16. codex_agent_framework-0.1.25/codex_agent/builtin_plugins/files/__init__.py +40 -0
  17. codex_agent_framework-0.1.25/codex_agent/builtin_plugins/files/requirements.txt +10 -0
  18. codex_agent_framework-0.1.25/codex_agent/builtin_plugins/image_generation/__init__.py +75 -0
  19. codex_agent_framework-0.1.25/codex_agent/builtin_plugins/image_generation/requirements.txt +3 -0
  20. codex_agent_framework-0.1.25/codex_agent/builtin_plugins/interface/requirements.txt +1 -0
  21. codex_agent_framework-0.1.25/codex_agent/builtin_plugins/memory/requirements.txt +2 -0
  22. codex_agent_framework-0.1.25/codex_agent/builtin_plugins/planner/requirements.txt +1 -0
  23. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/codex_agent/builtin_plugins/python/__init__.py +9 -0
  24. codex_agent_framework-0.1.25/codex_agent/builtin_plugins/python/requirements.txt +2 -0
  25. codex_agent_framework-0.1.25/codex_agent/builtin_plugins/realtime/__init__.py +131 -0
  26. codex_agent_framework-0.1.25/codex_agent/builtin_plugins/realtime/audio.py +382 -0
  27. codex_agent_framework-0.1.25/codex_agent/builtin_plugins/realtime/bridge.py +340 -0
  28. codex_agent_framework-0.1.25/codex_agent/builtin_plugins/realtime/manager.py +263 -0
  29. codex_agent_framework-0.1.25/codex_agent/builtin_plugins/realtime/requirements.txt +6 -0
  30. codex_agent_framework-0.1.25/codex_agent/builtin_plugins/realtime/session.py +275 -0
  31. codex_agent_framework-0.1.25/codex_agent/builtin_plugins/scheduler/requirements.txt +1 -0
  32. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/codex_agent/builtin_plugins/subagents/__init__.py +2 -1
  33. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/codex_agent/builtin_plugins/subagents/profiles/explorer.json +1 -2
  34. codex_agent_framework-0.1.25/codex_agent/builtin_plugins/subagents/requirements.txt +1 -0
  35. codex_agent_framework-0.1.25/codex_agent/builtin_plugins/tts/__init__.py +330 -0
  36. codex_agent_framework-0.1.25/codex_agent/builtin_plugins/tts/requirements.txt +4 -0
  37. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/codex_agent/cli/main.py +0 -1
  38. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/codex_agent/command.py +39 -13
  39. codex_agent_framework-0.1.25/codex_agent/config.py +257 -0
  40. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/codex_agent/event.py +100 -2
  41. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/codex_agent/hooks.py +25 -3
  42. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/codex_agent/mainloop.py +75 -3
  43. codex_agent_framework-0.1.25/codex_agent/plugin.py +559 -0
  44. codex_agent_framework-0.1.25/codex_agent/prompts/system_prompt.txt +55 -0
  45. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/codex_agent/provider.py +27 -9
  46. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/codex_agent/server/core.py +34 -2
  47. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/codex_agent/sessions.py +64 -13
  48. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/codex_agent/status.py +5 -0
  49. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/codex_agent/tool.py +34 -29
  50. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/codex_agent/tui/chat.py +211 -10
  51. codex_agent_framework-0.1.25/codex_agent/version.py +1 -0
  52. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25/codex_agent_framework.egg-info}/PKG-INFO +5 -3
  53. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/codex_agent_framework.egg-info/SOURCES.txt +23 -2
  54. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/pyproject.toml +2 -2
  55. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/tests/test_agent.py +851 -29
  56. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/tests/test_ai.py +44 -1
  57. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/tests/test_chat.py +165 -1
  58. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/tests/test_cli_headless.py +2 -2
  59. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/tests/test_server.py +21 -1
  60. codex_agent_framework-0.1.23/codex_agent/builtin_plugins/environment/__init__.py +0 -21
  61. codex_agent_framework-0.1.23/codex_agent/builtin_plugins/files/__init__.py +0 -28
  62. codex_agent_framework-0.1.23/codex_agent/config.py +0 -172
  63. codex_agent_framework-0.1.23/codex_agent/plugin.py +0 -248
  64. codex_agent_framework-0.1.23/codex_agent/prompts/system_prompt.txt +0 -83
  65. codex_agent_framework-0.1.23/codex_agent/version.py +0 -1
  66. codex_agent_framework-0.1.23/codex_agent/voice.py +0 -362
  67. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/LICENSE +0 -0
  68. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/MANIFEST.in +0 -0
  69. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/codex_agent/__main__.py +0 -0
  70. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/codex_agent/agent_runtime.py +0 -0
  71. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/codex_agent/browser.py +0 -0
  72. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/codex_agent/builtin_plugins/__init__.py +0 -0
  73. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/codex_agent/builtin_plugins/browser/__init__.py +0 -0
  74. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/codex_agent/builtin_plugins/content/system.py +0 -0
  75. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/codex_agent/builtin_plugins/content/vision.py +0 -0
  76. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/codex_agent/builtin_plugins/desktop/__init__.py +0 -0
  77. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/codex_agent/builtin_plugins/files/tools.py +0 -0
  78. {codex_agent_framework-0.1.23/codex_agent/prompts → codex_agent_framework-0.1.25/codex_agent/builtin_plugins/image_generation}/image_generation_system_prompt.txt +0 -0
  79. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/codex_agent/builtin_plugins/interface/__init__.py +0 -0
  80. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/codex_agent/builtin_plugins/memory/__init__.py +0 -0
  81. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/codex_agent/builtin_plugins/planner/__init__.py +0 -0
  82. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/codex_agent/builtin_plugins/scheduler/__init__.py +0 -0
  83. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/codex_agent/cli/__init__.py +0 -0
  84. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/codex_agent/cli/headless.py +0 -0
  85. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/codex_agent/cli/output.py +0 -0
  86. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/codex_agent/cli/root.py +0 -0
  87. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/codex_agent/cli/runner.py +0 -0
  88. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/codex_agent/client.py +0 -0
  89. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/codex_agent/context.py +0 -0
  90. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/codex_agent/desktop.py +0 -0
  91. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/codex_agent/get_text/__init__.py +0 -0
  92. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/codex_agent/get_text/default_gitignore +0 -0
  93. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/codex_agent/get_text/get_text.py +0 -0
  94. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/codex_agent/get_text/simpler_get_text.py +0 -0
  95. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/codex_agent/image.py +0 -0
  96. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/codex_agent/latex.py +0 -0
  97. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/codex_agent/message.py +0 -0
  98. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/codex_agent/runtime.py +0 -0
  99. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/codex_agent/scheduler.py +0 -0
  100. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/codex_agent/scripts/install-system-dependencies.sh +0 -0
  101. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/codex_agent/server/__init__.py +0 -0
  102. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/codex_agent/server/app.py +0 -0
  103. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/codex_agent/service.py +0 -0
  104. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/codex_agent/stream.py +0 -0
  105. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/codex_agent/stream_utils.py +0 -0
  106. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/codex_agent/tray.py +0 -0
  107. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/codex_agent/tui/__init__.py +0 -0
  108. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/codex_agent/tui/lifecycle.py +0 -0
  109. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/codex_agent/utils.py +0 -0
  110. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/codex_agent/worker.py +0 -0
  111. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/codex_agent_framework.egg-info/dependency_links.txt +0 -0
  112. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/codex_agent_framework.egg-info/entry_points.txt +0 -0
  113. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/codex_agent_framework.egg-info/requires.txt +0 -0
  114. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/codex_agent_framework.egg-info/top_level.txt +0 -0
  115. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/dependencies.txt +0 -0
  116. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/setup.cfg +0 -0
  117. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/tests/test_agent_runtime.py +0 -0
  118. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/tests/test_browser.py +0 -0
  119. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/tests/test_builtin_config.py +0 -0
  120. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/tests/test_cli.py +0 -0
  121. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/tests/test_cli_root.py +0 -0
  122. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/tests/test_client.py +0 -0
  123. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/tests/test_events.py +0 -0
  124. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/tests/test_get_text_browser.py +0 -0
  125. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/tests/test_hooks.py +0 -0
  126. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/tests/test_image_message.py +0 -0
  127. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/tests/test_local_desktop.py +0 -0
  128. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/tests/test_memory.py +0 -0
  129. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/tests/test_messages.py +0 -0
  130. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/tests/test_planner.py +0 -0
  131. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/tests/test_scheduler.py +0 -0
  132. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/tests/test_service.py +0 -0
  133. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/tests/test_tray.py +0 -0
  134. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/tests/test_tui.py +0 -0
  135. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/tests/test_utils.py +0 -0
  136. {codex_agent_framework-0.1.23 → codex_agent_framework-0.1.25}/tests/test_worker.py +0 -0
@@ -4,6 +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.25] - 2026-05-12
8
+ ### Added
9
+ - Add bundled plugin `requirements.txt` metadata for built-in capabilities and move image generation, realtime voice, and TTS into plugin-scoped packages.
10
+ - Add a selectable Textual TUI conversation pane with mouse scrolling, Ctrl+C selection copy, lazy older-transcript loading, and bottom-aware auto-scroll during streaming.
11
+
12
+ ### Changed
13
+ - Continue migrating built-in capability instructions and dependencies toward the plugin-first architecture.
14
+ - Remove legacy voice and prompt files that are now owned by dedicated built-in plugins.
15
+
16
+ ### Tests
17
+ - Expand regression coverage across agent/plugin/runtime/chat behavior; the current suite validates at 546 passed.
18
+
19
+ ## [0.1.24] - 2026-05-10
20
+ ### Added
21
+ - Add dedicated slash-command lifecycle events, `SlashCommandStartEvent` and `SlashCommandDoneEvent`, with command name, arguments, status, result, and error metadata.
22
+
23
+ ### Changed
24
+ - Show slash-command progress directly in the Textual TUI with running/done/failed status lines so commands such as `/compact` and `/help` no longer appear silent while executing.
25
+ - Render slash-command results and errors immediately in the TUI after completion.
26
+
27
+ ### Tests
28
+ - Add regression coverage for slash-command lifecycle events, failed command reporting, and TUI rendering of command progress, results, and errors.
29
+
7
30
  ## [0.1.23] - 2026-05-10
8
31
  ### Added
9
32
  - Add the SDK `Agent.stream(prompt=None)` API for iterating turn events while still exposing the final `AgentTurn` through `.turn`.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codex-agent-framework
3
- Version: 0.1.23
3
+ Version: 0.1.25
4
4
  Summary: A lightweight event-driven Codex agent runtime.
5
5
  Author: Baptiste
6
6
  License-Expression: MIT
@@ -567,7 +567,7 @@ logs/ detached server/tray logs
567
567
  memory.json durable semantic memory entries
568
568
  planner.json persistent named todos
569
569
  wakeups.json scheduled autonomous wakeups
570
- agent_config.json persisted runtime configuration
570
+ agent.config.json persisted agent configuration
571
571
  ```
572
572
 
573
573
  Override it with:
@@ -610,7 +610,7 @@ python -m pytest
610
610
  The current tree validates at:
611
611
 
612
612
  ```text
613
- 489 passed
613
+ 546 passed
614
614
  ```
615
615
 
616
616
  Build distributions:
@@ -624,6 +624,8 @@ python -m twine check dist/*
624
624
 
625
625
  ## Recent releases
626
626
 
627
+ - `0.1.25`: move built-in capabilities further into plugin-scoped modules, add bundled plugin requirement metadata, and replace the TUI conversation pane with a selectable rich chat area that preserves mouse scrolling, copy selection, lazy transcript loading, and bottom-aware auto-scroll.
628
+ - `0.1.24`: add slash-command lifecycle events and TUI progress/result rendering so commands such as `/compact` and `/help` show running, done, failed, result, and error feedback instead of going silent.
627
629
  - `0.1.23`: add SDK event streaming with `Agent.stream()`, root `Agent.add_plugin()`, a faster README flow, the `~/.codex-agent` runtime default, text-first voice defaults, safer prompt templating limited to system messages, smarter `codex-agent` local-server attachment, and clearer TUI tool-call progress lines.
628
630
  - `0.1.22`: harden FastAPI/SSE and TUI streaming after a stabilization audit; add finite SSE read timeouts, stale-subscriber shutdown, persisted turn-event locking, synchronous replay delta flushes, process-runtime startup guards, isolated event handlers, and shared version metadata.
629
631
  - `0.1.21`: add structured `AgentTurn` returns with messages/events, resumable non-terminal turns for `auto_proceed=False`, profile-based subagents, hierarchical message/event types, and internal wakeups as developer messages.
@@ -518,7 +518,7 @@ logs/ detached server/tray logs
518
518
  memory.json durable semantic memory entries
519
519
  planner.json persistent named todos
520
520
  wakeups.json scheduled autonomous wakeups
521
- agent_config.json persisted runtime configuration
521
+ agent.config.json persisted agent configuration
522
522
  ```
523
523
 
524
524
  Override it with:
@@ -561,7 +561,7 @@ python -m pytest
561
561
  The current tree validates at:
562
562
 
563
563
  ```text
564
- 489 passed
564
+ 546 passed
565
565
  ```
566
566
 
567
567
  Build distributions:
@@ -575,6 +575,8 @@ python -m twine check dist/*
575
575
 
576
576
  ## Recent releases
577
577
 
578
+ - `0.1.25`: move built-in capabilities further into plugin-scoped modules, add bundled plugin requirement metadata, and replace the TUI conversation pane with a selectable rich chat area that preserves mouse scrolling, copy selection, lazy transcript loading, and bottom-aware auto-scroll.
579
+ - `0.1.24`: add slash-command lifecycle events and TUI progress/result rendering so commands such as `/compact` and `/help` show running, done, failed, result, and error feedback instead of going silent.
578
580
  - `0.1.23`: add SDK event streaming with `Agent.stream()`, root `Agent.add_plugin()`, a faster README flow, the `~/.codex-agent` runtime default, text-first voice defaults, safer prompt templating limited to system messages, smarter `codex-agent` local-server attachment, and clearer TUI tool-call progress lines.
579
581
  - `0.1.22`: harden FastAPI/SSE and TUI streaming after a stabilization audit; add finite SSE read timeouts, stale-subscriber shutdown, persisted turn-event locking, synchronous replay delta flushes, process-runtime startup guards, isolated event handlers, and shared version metadata.
580
582
  - `0.1.21`: add structured `AgentTurn` returns with messages/events, resumable non-terminal turns for `auto_proceed=False`, profile-based subagents, hierarchical message/event types, and internal wakeups as developer messages.
@@ -13,6 +13,7 @@ from .event import (
13
13
  AssistantTurnStartEvent,
14
14
  AudioPlaybackEvent,
15
15
  CompactionCompletedEvent,
16
+ CompactionFailedEvent,
16
17
  CompactionRequestedEvent,
17
18
  Event,
18
19
  EventBus,
@@ -25,8 +26,17 @@ from .event import (
25
26
  ResponseReasoningDeltaEvent,
26
27
  ResponseStartEvent,
27
28
  ResponseToolCallsDeltaEvent,
29
+ RealtimeApiEvent,
30
+ RealtimeErrorEvent,
31
+ RealtimeSessionRefreshEvent,
32
+ RealtimeStartedEvent,
33
+ RealtimeStartingEvent,
34
+ RealtimeStoppedEvent,
35
+ RuntimeStateChangedEvent,
28
36
  ServerToolCallEvent,
29
37
  SessionDeletedEvent,
38
+ SlashCommandDoneEvent,
39
+ SlashCommandStartEvent,
30
40
  SubagentEvent,
31
41
  SessionLoadedEvent,
32
42
  ToolCallDoneEvent,
@@ -48,14 +58,14 @@ from .message import (
48
58
  )
49
59
  from .image import ImageMessage
50
60
  from .mainloop import MainLoopManager
51
- from .plugin import Plugin, PluginsManager, event_handler, hook_handler
61
+ from .plugin import Plugin, PluginsManager, event_handler, hook_handler, stream_processor
52
62
  from .command import AgentCommand, AgentFuture, AgentTurn, CommandManager, command
53
63
  from .stream import AgentStream
54
64
  from .provider import ProvidersManager, provider
55
65
  from .runtime import RuntimeManager
56
66
  from .sessions import AgentSession, SessionsManager
57
67
  from .status import StatusManager
58
- from .tool import ImageGenerationTool, ServerTool, Tool, ToolsManager, WebSearchTool, get_agent, get_current_call_id, tool
68
+ from .tool import ServerTool, Tool, ToolsManager, WebSearchTool, get_agent, get_current_call_id, tool
59
69
  from .worker import SummarizerWorker, TurnSummaryWorker, Worker
60
70
  from .version import __version__
61
71
 
@@ -76,6 +86,7 @@ __all__ = [
76
86
  "AssistantTurnStartEvent",
77
87
  "AudioPlaybackEvent",
78
88
  "CompactionCompletedEvent",
89
+ "CompactionFailedEvent",
79
90
  "CompactionRequestedEvent",
80
91
  "Event",
81
92
  "EventBus",
@@ -88,8 +99,17 @@ __all__ = [
88
99
  "ResponseReasoningDeltaEvent",
89
100
  "ResponseStartEvent",
90
101
  "ResponseToolCallsDeltaEvent",
102
+ "RealtimeApiEvent",
103
+ "RealtimeErrorEvent",
104
+ "RealtimeSessionRefreshEvent",
105
+ "RealtimeStartedEvent",
106
+ "RealtimeStartingEvent",
107
+ "RealtimeStoppedEvent",
108
+ "RuntimeStateChangedEvent",
91
109
  "ServerToolCallEvent",
92
110
  "SessionDeletedEvent",
111
+ "SlashCommandDoneEvent",
112
+ "SlashCommandStartEvent",
93
113
  "SubagentEvent",
94
114
  "SessionLoadedEvent",
95
115
  "ToolCallDoneEvent",
@@ -112,6 +132,7 @@ __all__ = [
112
132
  "PluginsManager",
113
133
  "event_handler",
114
134
  "hook_handler",
135
+ "stream_processor",
115
136
  "ProvidersManager",
116
137
  "AgentCommand",
117
138
  "AgentFuture",
@@ -124,7 +145,6 @@ __all__ = [
124
145
  "AgentSession",
125
146
  "SessionsManager",
126
147
  "StatusManager",
127
- "ImageGenerationTool",
128
148
  "ServerTool",
129
149
  "Tool",
130
150
  "ToolsManager",
@@ -1,6 +1,7 @@
1
1
  from .utils import text_content, ensure_runtime_dir, upload_file_to_folder, project_python_env, runtime_join, root_join
2
2
  import os
3
- from .message import DeveloperMessage, ServerToolResponseMessage, SystemMessage, ToolResultMessage, UserMessage, message_from_data
3
+ from modict import modict
4
+ from .message import DeveloperMessage, ServerToolResponseMessage, ToolResultMessage, UserMessage, message_from_data
4
5
  from .image import ImageMessage
5
6
  from .command import CommandManager
6
7
  from .config import ConfigManager
@@ -13,11 +14,9 @@ from .runtime import RuntimeManager
13
14
  from .scheduler import AgentScheduler
14
15
  from .sessions import SessionsManager
15
16
  from .status import StatusManager
16
- from .tool import ImageGenerationTool, ToolsManager, reset_current_agent, set_current_agent
17
+ from .tool import ToolsManager, reset_current_agent, set_current_agent
17
18
  from .ai import AIClient
18
- from .voice import VoiceProcessor
19
- from .latex import LaTeXProcessor
20
- from .event import AssistantInterruptRequestedEvent, AssistantInterrupted, AssistantInterruptedEvent, AudioPlaybackEvent, EventBus
19
+ from .event import AssistantInterruptRequestedEvent, AssistantInterrupted, AssistantInterruptedEvent, EventBus
21
20
  from threading import Event as ThreadEvent
22
21
 
23
22
  class Agent:
@@ -51,7 +50,8 @@ class Agent:
51
50
  self.runtime=RuntimeManager(self)
52
51
  self.scheduler=AgentScheduler(self)
53
52
  self.hooks=HookManager(self)
54
- self.on(AudioPlaybackEvent, self.handle_audio_playback)
53
+ self.stream_processors=modict()
54
+ self.stream_processor_owners={}
55
55
  self.mainloop_manager=MainLoopManager(self)
56
56
  self._interrupt_requested=ThreadEvent()
57
57
  self.current_session_id=None
@@ -63,9 +63,6 @@ class Agent:
63
63
  self.command_manager.init_folder()
64
64
  self.plugins_manager.init_folder()
65
65
  self.ai=AIClient(self)
66
- self.voice=VoiceProcessor(self)
67
- self.latex=LaTeXProcessor()
68
- self.content_processors=[self.voice]
69
66
  self.command_manager.init_builtin()
70
67
  self.plugins_manager.init_builtin()
71
68
  self.plugins_manager.init_runtime()
@@ -74,13 +71,13 @@ class Agent:
74
71
  self.tools_manager.apply_config_exclusions()
75
72
  self.start_new_session() if session == "new" else self.load_session(session)
76
73
 
77
- def on(self, event_type, handler=None):
74
+ def on(self, event_type, handler=None, owner=None):
78
75
  """Register an event handler.
79
76
 
80
77
  Can be used as ``agent.on(ResponseContentDeltaEvent, handler)`` or as
81
78
  ``@agent.on(ResponseContentDeltaEvent)``.
82
79
  """
83
- return self.events.on(event_type, handler)
80
+ return self.events.on(event_type, handler, owner=owner)
84
81
 
85
82
  def emit(self, event_type, **data):
86
83
  event = self.events.emit(event_type, **data)
@@ -203,8 +200,13 @@ class Agent:
203
200
  def save_session(self):
204
201
  return self.sessions_manager.save()
205
202
 
206
- def compact_session(self, client=None, model=None, instructions=None):
207
- return self.sessions_manager.compact(client=client, model=model, instructions=instructions)
203
+ def compact_session(self, client=None, model=None, instructions=None, max_completed_turns=None):
204
+ return self.sessions_manager.compact(
205
+ client=client,
206
+ model=model,
207
+ instructions=instructions,
208
+ max_completed_turns=max_completed_turns,
209
+ )
208
210
 
209
211
  def add_message(self,msg,pending=False):
210
212
  """Queue a message for the next pending flush, except tool results."""
@@ -238,7 +240,7 @@ class Agent:
238
240
  self.add_message(img,pending=pending)
239
241
  return img
240
242
 
241
- def add_tool(self,func=None,name=None,description=None,parameters=None,required=None,tool_type=None,format=None):
243
+ def add_tool(self,func=None,name=None,description=None,parameters=None,required=None,tool_type=None,format=None,owner=None):
242
244
  return self.tools_manager.add(
243
245
  func=func,
244
246
  name=name,
@@ -247,18 +249,59 @@ class Agent:
247
249
  required=required,
248
250
  tool_type=tool_type,
249
251
  format=format,
252
+ owner=owner,
250
253
  )
251
254
 
252
- def add_provider(self, func=None, name=None):
253
- return self.providers_manager.add(func=func, name=name)
255
+ def add_provider(self, func=None, name=None, owner=None):
256
+ return self.providers_manager.add(func=func, name=name, owner=owner)
254
257
 
255
- def add_command(self, func=None, name=None):
256
- return self.command_manager.add(func, name=name)
258
+ def add_command(self, func=None, name=None, owner=None):
259
+ return self.command_manager.add(func, name=name, owner=owner)
257
260
 
258
261
  def add_plugin(self, plugin):
259
262
  return self.plugins_manager.add_plugin(plugin)
260
263
 
261
- def add_hook(self, key, func=None):
264
+ def load_builtin_plugin(self, name):
265
+ return self.plugins_manager.load_builtin_plugin(name)
266
+
267
+ def load_runtime_plugin(self, name_or_path):
268
+ return self.plugins_manager.load_runtime_plugin(name_or_path)
269
+
270
+ def unload_plugin(self, plugin_or_name, timeout=None):
271
+ return self.plugins_manager.unload_plugin(plugin_or_name, timeout=timeout)
272
+
273
+ def reload_plugin(self, plugin_or_name, timeout=None):
274
+ return self.plugins_manager.reload_plugin(plugin_or_name, timeout=timeout)
275
+
276
+ def add_stream_processor(self, key, processor, owner=None):
277
+ processors = self.stream_processors.setdefault(str(key), [])
278
+ processors.append(processor)
279
+ if owner is not None:
280
+ self.stream_processor_owners.setdefault(str(key), {})[processor] = owner
281
+ return processor
282
+
283
+ def get_stream_processors(self, key):
284
+ return list(self.stream_processors.get(str(key), []))
285
+
286
+ def remove_stream_processors_by_owner(self, owner):
287
+ removed = modict()
288
+ for key, processors in list(self.stream_processors.items()):
289
+ owners = self.stream_processor_owners.get(key, {})
290
+ kept = []
291
+ for processor in processors:
292
+ if owners.get(processor) == owner:
293
+ removed.setdefault(key, []).append(processor)
294
+ owners.pop(processor, None)
295
+ else:
296
+ kept.append(processor)
297
+ if kept:
298
+ self.stream_processors[key] = kept
299
+ else:
300
+ self.stream_processors.pop(key, None)
301
+ self.stream_processor_owners.pop(key, None)
302
+ return removed
303
+
304
+ def add_hook(self, key, func=None, owner=None):
262
305
  """Register a runtime hook.
263
306
 
264
307
  Hooks are callable extension points keyed by name. They are runtime
@@ -267,13 +310,13 @@ class Agent:
267
310
  """
268
311
  if func is None:
269
312
  def decorator(f):
270
- return self.add_hook(key, f)
313
+ return self.add_hook(key, f, owner=owner)
271
314
  return decorator
272
315
 
273
316
  if hasattr(self, "mainloop_manager") and self.mainloop_manager.should_queue_state_command():
274
- return self.submit("add_hook", key=key, func=func).wait().result
317
+ return self.submit("add_hook", key=key, func=func, owner=owner).wait().result
275
318
 
276
- return self.hooks.add(key, func)
319
+ return self.hooks.add(key, func, owner=owner)
277
320
 
278
321
  def has_hook(self, key):
279
322
  return self.hooks.has(key)
@@ -298,44 +341,6 @@ class Agent:
298
341
  def get_status(self):
299
342
  return self.status_manager.get_status()
300
343
 
301
- def create_image(
302
- self,
303
- prompt,
304
- input_images=None,
305
- model="gpt-5.4",
306
- reasoning_effort="medium",
307
- verbosity="medium",
308
- output_format="png",
309
- size=None,
310
- quality=None,
311
- background=None,
312
- moderation=None,
313
- ):
314
- """Generate an image directly and return the saved image path."""
315
- content = [{"type": "text", "text": prompt}]
316
- for source in input_images or []:
317
- content.extend(ImageMessage(content=source).content_to_response_parts())
318
-
319
- system_prompt = text_content(os.path.join(os.path.dirname(__file__), "prompts", "image_generation_system_prompt.txt"))
320
- message = self.ai.run(
321
- messages=[SystemMessage(content=system_prompt), UserMessage(content=content)],
322
- tools=[ImageGenerationTool(
323
- output_format=output_format,
324
- size=size,
325
- quality=quality,
326
- background=background,
327
- moderation=moderation,
328
- ).to_response_tool()],
329
- model=model,
330
- reasoning_effort=reasoning_effort,
331
- verbosity=verbosity,
332
- )
333
- for item in message.output_items:
334
- if item.get("type") == "image_generation_call":
335
- return ServerToolResponseMessage(item=item).content
336
- else:
337
- raise RuntimeError("The image_generation tool did not return an image.")
338
-
339
344
  def get_response(self):
340
345
  """Run the model, emit response events, and store the final message.
341
346
 
@@ -366,20 +371,6 @@ class Agent:
366
371
  self.sessions_manager.append_message(ServerToolResponseMessage(item=item))
367
372
  return message
368
373
 
369
- def speak(self,text,**kwargs):
370
- audio=self.ai.text_to_audio(text,**kwargs)
371
- if audio is not None:
372
- self.emit(AudioPlaybackEvent, audio=audio)
373
-
374
- def handle_audio_playback(self, event):
375
- audio = event.get("audio")
376
- if audio is None:
377
- return None
378
- if self.has_hook("audio_playback_hook"):
379
- return self.run_hooks("audio_playback_hook", audio)
380
- from .voice import silent_play
381
- return silent_play(audio)
382
-
383
374
  def listen(self, source, language=None, **kwargs):
384
375
  """
385
376
  description: |
@@ -1,3 +1,6 @@
1
+ import queue
2
+ import threading
3
+
1
4
  import numpy as np
2
5
  from .message import AssistantMessage, MessageChunk, SystemMessage
3
6
  from .utils import truncate
@@ -41,7 +44,7 @@ class ChunkStreamer(MappingStreamProcessor):
41
44
  self.agent=agent
42
45
  super().__init__(
43
46
  defaults=dict(content='',reasoning='',tool_calls=None,output_items=None),
44
- processors=dict(content=self.agent.content_processors),
47
+ processors=dict(content=self.agent.get_stream_processors("content")),
45
48
  type=MessageChunk,
46
49
  threaded=threaded
47
50
  )
@@ -254,7 +257,8 @@ class AIClient:
254
257
 
255
258
  try:
256
259
  tool_call_index = 0
257
- for event in response:
260
+ for event in self._iter_response_events(response):
261
+ self.agent.mainloop_manager.touch_activity("response_stream")
258
262
  if isinstance(event, ResponseCompleted):
259
263
  self.agent.status_manager.cache_api_status(
260
264
  usage=event.usage,
@@ -274,6 +278,41 @@ class AIClient:
274
278
  raise AIBadRequestError(str(e)) from e
275
279
  yield MessageChunk(content=f"Hmmm, sorry! There was an error while reading the Codex backend stream. Here is the error message I got:\n\n ```\n{str(e)}\n```")
276
280
 
281
+ def _iter_response_events(self, response):
282
+ timeout = float(self.agent.config.get("codex_stream_idle_timeout", 0) or 0)
283
+ if timeout <= 0:
284
+ yield from response
285
+ return
286
+
287
+ events = queue.Queue()
288
+ sentinel = object()
289
+
290
+ def consume():
291
+ try:
292
+ for event in response:
293
+ events.put(("event", event))
294
+ except BaseException as exc:
295
+ events.put(("error", exc))
296
+ finally:
297
+ events.put(("done", sentinel))
298
+
299
+ threading.Thread(target=consume, name="codex-response-stream", daemon=True).start()
300
+ while True:
301
+ try:
302
+ kind, value = events.get(timeout=timeout)
303
+ except queue.Empty as exc:
304
+ try:
305
+ self.abort()
306
+ finally:
307
+ raise AIClientError(
308
+ f"Codex backend stream produced no events for {timeout:g}s"
309
+ ) from exc
310
+ if kind == "done":
311
+ break
312
+ if kind == "error":
313
+ raise value
314
+ yield value
315
+
277
316
  def is_bad_request_error(self, exc):
278
317
  response = getattr(exc, "response", None)
279
318
  status_code = getattr(response, "status_code", None)
@@ -53,15 +53,20 @@ def help():
53
53
 
54
54
 
55
55
  @command
56
- def compact():
56
+ def compact(max_completed_turns=None):
57
57
  agent = get_agent()
58
+ max_completed_turns = None if max_completed_turns in (None, "", "all") else int(max_completed_turns)
58
59
  message = agent.compact_session(
59
60
  model=agent.config.model,
60
61
  instructions=agent.config_manager.system_message().content,
62
+ max_completed_turns=max_completed_turns,
61
63
  )
62
64
  if message is None:
63
65
  return "Nothing to compact."
64
- return f"Compacted {message.compacted_message_count} messages."
66
+ if message.get("status") == "failed":
67
+ return f"Compaction failed: {message.error}"
68
+ suffix = "" if max_completed_turns is None else f" from {max_completed_turns} completed turn(s)"
69
+ return f"Compacted {message.compacted_message_count} messages{suffix}."
65
70
 
66
71
 
67
72
  @command
@@ -134,22 +139,3 @@ def verbosity(value=None):
134
139
  if value is None:
135
140
  return f"verbosity: {get_agent().config.verbosity!r}"
136
141
  return update_model_config(verbosity=value)
137
-
138
-
139
- @command
140
- def voice(value=None):
141
- agent = get_agent()
142
- if value is None:
143
- state = "on" if agent.config.get("voice_enabled", False) else "off"
144
- return f"voice: {state}; voice={agent.config.get('voice')!r}"
145
-
146
- lowered = str(value).lower()
147
- if lowered in ("on", "true", "yes", "1"):
148
- agent.config_manager.update(voice_enabled=True)
149
- return f"Updated config: voice_enabled={agent.config.voice_enabled!r}"
150
- if lowered in ("off", "false", "no", "0"):
151
- agent.config_manager.update(voice_enabled=False)
152
- return f"Updated config: voice_enabled={agent.config.voice_enabled!r}"
153
-
154
- agent.config_manager.update(voice=str(value), voice_enabled=True)
155
- return f"Updated config: voice={agent.config.voice!r}, voice_enabled={agent.config.voice_enabled!r}"
@@ -11,6 +11,13 @@ class BashPlugin(Plugin):
11
11
  name = "bash"
12
12
  description = "Run Bash commands on the local system."
13
13
  prefix_tools = False
14
+ system_prompt = """
15
+ # Bash Tool
16
+
17
+ Via the `bash` tool you may execute shell commands on the user's local system. This can be useful to act on the local environment, inspect project state, or access the internet when appropriate.
18
+
19
+ Pass raw shell script text to the tool, not JSON, quotes, or a markdown code fence. Commands run with user privileges and the agent execution environment, so use discernment before running commands that could compromise sensitive data or system integrity.
20
+ """
14
21
 
15
22
  @tool(name="bash", tool_type="custom")
16
23
  def bash(self, content=None):
@@ -0,0 +1 @@
1
+ # No plugin-specific Python dependencies.
@@ -0,0 +1,2 @@
1
+ # Persistent Chromium browser automation.
2
+ playwright
@@ -11,6 +11,16 @@ class ContentPlugin(Plugin):
11
11
  name = "content"
12
12
  description = "Extract, observe, or open content from varied sources."
13
13
  prefix_tools = False
14
+ system_prompt = """
15
+ # Content Tools
16
+
17
+ Use the specialized content tools to inspect or present non-plain-text sources and visual material:
18
+ - `view`: quickly access extracted content from folders, URLs, PDFs, DOCX, ODT, HTML, XLSX, archives, and other rich sources. It is convenient and broad, but not a strict source-of-truth reader for local UTF-8 files.
19
+ - `observe`: use AI vision capabilities on image files, screenshots, windows, screens, or URLs.
20
+ - `show`: open a local file, folder, or URL with the system default application/browser when the user should visually inspect it outside the chat.
21
+
22
+ Use `view` for broad extraction and discovery, but prefer the file plugin's `read` tool when you need faithful line-numbered local UTF-8 file content before editing.
23
+ """
14
24
 
15
25
  @tool(name="view")
16
26
  def view(self, source, start_at_line: int = 1, search_query: str = None):
@@ -0,0 +1,11 @@
1
+ # Rich document/content extraction and visual inspection.
2
+ beautifulsoup4
3
+ filetype
4
+ odfpy
5
+ openpyxl
6
+ pillow
7
+ pypdf
8
+ python-docx
9
+ PyYAML
10
+ requests
11
+ trafilatura
@@ -0,0 +1,4 @@
1
+ # Local desktop automation and screenshots.
2
+ mss
3
+ pillow
4
+ pywinctl