codex-agent-framework 0.1.13__tar.gz → 0.1.15__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 (105) hide show
  1. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/CHANGELOG.md +35 -0
  2. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/MANIFEST.in +2 -0
  3. {codex_agent_framework-0.1.13/codex_agent_framework.egg-info → codex_agent_framework-0.1.15}/PKG-INFO +47 -8
  4. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/README.md +46 -7
  5. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/codex_agent/__init__.py +2 -0
  6. codex_agent_framework-0.1.15/codex_agent/__main__.py +7 -0
  7. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/codex_agent/agent.py +31 -23
  8. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/codex_agent/agent_runtime.py +152 -3
  9. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/codex_agent/ai.py +82 -9
  10. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/codex_agent/builtin_commands.py +1 -0
  11. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/codex_agent/builtin_plugins/browser/__init__.py +29 -21
  12. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/codex_agent/builtin_plugins/browser/controller.py +48 -8
  13. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/codex_agent/builtin_plugins/desktop/__init__.py +113 -301
  14. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/codex_agent/builtin_plugins/desktop/controller.py +34 -11
  15. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/codex_agent/builtin_plugins/memory/__init__.py +33 -10
  16. codex_agent_framework-0.1.15/codex_agent/builtin_plugins/planner/__init__.py +266 -0
  17. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/codex_agent/builtin_plugins/scheduler/__init__.py +12 -4
  18. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/codex_agent/builtin_tools/files.py +23 -2
  19. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/codex_agent/builtin_tools/shell.py +8 -2
  20. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/codex_agent/chat.py +30 -5
  21. codex_agent_framework-0.1.15/codex_agent/cli/__init__.py +1 -0
  22. codex_agent_framework-0.1.15/codex_agent/cli/headless.py +203 -0
  23. codex_agent_framework-0.1.15/codex_agent/cli/main.py +182 -0
  24. codex_agent_framework-0.1.15/codex_agent/cli/output.py +31 -0
  25. codex_agent_framework-0.1.15/codex_agent/cli/root.py +169 -0
  26. codex_agent_framework-0.1.15/codex_agent/cli/runner.py +87 -0
  27. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/codex_agent/client.py +20 -2
  28. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/codex_agent/config.py +38 -8
  29. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/codex_agent/context.py +6 -1
  30. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/codex_agent/event.py +5 -0
  31. codex_agent_framework-0.1.15/codex_agent/hooks.py +71 -0
  32. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/codex_agent/mainloop.py +44 -18
  33. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/codex_agent/memory.py +13 -2
  34. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/codex_agent/message.py +6 -0
  35. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/codex_agent/plugin.py +23 -1
  36. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/codex_agent/prompts/system_prompt.txt +1 -14
  37. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/codex_agent/registry.py +24 -7
  38. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/codex_agent/scheduler.py +7 -11
  39. codex_agent_framework-0.1.15/codex_agent/scripts/install-system-dependencies.sh +149 -0
  40. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/codex_agent/server.py +20 -26
  41. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/codex_agent/service.py +4 -3
  42. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/codex_agent/sessions.py +36 -0
  43. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/codex_agent/utils.py +21 -1
  44. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15/codex_agent_framework.egg-info}/PKG-INFO +47 -8
  45. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/codex_agent_framework.egg-info/SOURCES.txt +16 -0
  46. codex_agent_framework-0.1.15/codex_agent_framework.egg-info/entry_points.txt +2 -0
  47. codex_agent_framework-0.1.15/dependencies.txt +88 -0
  48. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/pyproject.toml +3 -3
  49. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/tests/test_agent.py +314 -124
  50. codex_agent_framework-0.1.15/tests/test_agent_runtime.py +121 -0
  51. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/tests/test_ai.py +49 -1
  52. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/tests/test_browser.py +33 -0
  53. codex_agent_framework-0.1.15/tests/test_builtin_config.py +33 -0
  54. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/tests/test_chat.py +14 -8
  55. codex_agent_framework-0.1.15/tests/test_cli.py +121 -0
  56. codex_agent_framework-0.1.15/tests/test_cli_headless.py +136 -0
  57. codex_agent_framework-0.1.15/tests/test_cli_root.py +106 -0
  58. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/tests/test_client.py +2 -2
  59. codex_agent_framework-0.1.15/tests/test_hooks.py +188 -0
  60. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/tests/test_local_desktop.py +62 -0
  61. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/tests/test_memory.py +125 -3
  62. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/tests/test_messages.py +16 -0
  63. codex_agent_framework-0.1.15/tests/test_planner.py +133 -0
  64. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/tests/test_server.py +58 -47
  65. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/tests/test_service.py +4 -2
  66. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/tests/test_utils.py +12 -2
  67. codex_agent_framework-0.1.13/codex_agent/__main__.py +0 -106
  68. codex_agent_framework-0.1.13/codex_agent_framework.egg-info/entry_points.txt +0 -2
  69. codex_agent_framework-0.1.13/tests/test_cli.py +0 -65
  70. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/LICENSE +0 -0
  71. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/codex_agent/builtin_plugins/__init__.py +0 -0
  72. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/codex_agent/builtin_plugins/desktop/screenshot.py +0 -0
  73. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/codex_agent/builtin_providers.py +0 -0
  74. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/codex_agent/builtin_tools/__init__.py +0 -0
  75. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/codex_agent/builtin_tools/server_tools.py +0 -0
  76. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/codex_agent/builtin_tools/system.py +0 -0
  77. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/codex_agent/builtin_tools/vision.py +0 -0
  78. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/codex_agent/command.py +0 -0
  79. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/codex_agent/get_text/__init__.py +0 -0
  80. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/codex_agent/get_text/default_gitignore +0 -0
  81. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/codex_agent/get_text/get_text.py +0 -0
  82. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/codex_agent/get_text/simpler_get_text.py +0 -0
  83. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/codex_agent/image.py +0 -0
  84. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/codex_agent/latex.py +0 -0
  85. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/codex_agent/prompts/image_generation_system_prompt.txt +0 -0
  86. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/codex_agent/provider.py +0 -0
  87. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/codex_agent/runtime.py +0 -0
  88. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/codex_agent/status.py +0 -0
  89. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/codex_agent/stream_utils.py +0 -0
  90. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/codex_agent/tool.py +0 -0
  91. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/codex_agent/tray.py +0 -0
  92. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/codex_agent/tui.py +0 -0
  93. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/codex_agent/voice.py +0 -0
  94. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/codex_agent/worker.py +0 -0
  95. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/codex_agent_framework.egg-info/dependency_links.txt +0 -0
  96. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/codex_agent_framework.egg-info/requires.txt +0 -0
  97. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/codex_agent_framework.egg-info/top_level.txt +0 -0
  98. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/setup.cfg +0 -0
  99. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/tests/test_events.py +0 -0
  100. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/tests/test_get_text_browser.py +0 -0
  101. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/tests/test_image_message.py +0 -0
  102. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/tests/test_scheduler.py +0 -0
  103. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/tests/test_tray.py +0 -0
  104. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/tests/test_tui.py +0 -0
  105. {codex_agent_framework-0.1.13 → codex_agent_framework-0.1.15}/tests/test_worker.py +0 -0
@@ -4,6 +4,41 @@ 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.15] - 2026-05-08
8
+ ### Added
9
+ - Add `AgentRuntime` as the canonical external runtime contract for HTTP server, headless CLI, in-process, and process-backed integrations.
10
+ - Add structured runtime configuration updates through `PATCH /config`, `AgentClient.update_config()`, and `codex-agent config set`.
11
+ - Add direct built-in selection config for tools, providers, and plugins with `None` meaning all built-ins, an empty list meaning none, and explicit lists selecting named built-ins.
12
+
13
+ ### Changed
14
+ - Split the command line implementation into the `codex_agent.cli` package with root orchestration commands and headless runtime commands.
15
+ - Route server operations through `AgentRuntime` instead of depending on `Agent` internals directly.
16
+ - Update CLI service/interface commands to the explicit `start/stop/restart server|tray` and `open/close tui` forms.
17
+
18
+ ### Tests
19
+ - Add regression coverage for the runtime abstraction, headless CLI, root CLI dispatch, config updates, and built-in selection.
20
+ - Validate the targeted release-regression suite at 82 passing tests.
21
+
22
+ ## [0.1.14] - 2026-05-07
23
+ ### Added
24
+ - Add a general `HookManager` with compatibility helpers and hook sites around message submission, commands, context/provider calls, runtime module loading, tool calls, shell execution, and file write/edit operations.
25
+ - Add planner and scheduler built-in plugins with atomic JSON persistence safeguards.
26
+ - Add `dependencies.txt` documenting non-Python system dependencies for browser, desktop, tray, service, TUI terminal, and audio features.
27
+ - Add packaged system dependency installation via `codex-agent install-system-deps` and a higher-level `codex-agent bootstrap` command that can install system dependencies then install/start the user services.
28
+
29
+ ### Changed
30
+ - Improve TUI SSE replay/reconnect handling so replayed deltas are rendered as a compact catch-up while live deltas continue streaming normally.
31
+ - Keep the TUI reconnection status concise by reporting one offline message per outage and a connected message on recovery.
32
+
33
+ ### Fixed
34
+ - Fix SSE cursor tracking so client replay state advances only from real sequenced events, avoiding missed events after reconnects.
35
+ - Surface asynchronous memory archive failures through `MemoryArchiveErrorEvent` instead of swallowing them silently.
36
+ - Clarify scheduler wakeup trigger event data around `turn_id` while preserving backward compatibility.
37
+
38
+ ### Tests
39
+ - Expand regression coverage for hooks, planner schema/loading, scheduler persistence, TUI reconnect/replay behavior, CLI bootstrap/system dependency commands, and memory archive errors.
40
+ - Validate the full suite at 412 passing tests.
41
+
7
42
  ## [0.1.13] - 2026-05-07
8
43
  ### Changed
9
44
  - Reorganize built-in tools into a `codex_agent.builtin_tools` package with separate modules for file, shell, vision, system, and server-tool responsibilities.
@@ -3,5 +3,7 @@ include LICENSE
3
3
  include CHANGELOG.md
4
4
  recursive-include codex_agent/prompts *.txt
5
5
  include codex_agent/get_text/default_gitignore
6
+ recursive-include codex_agent/scripts *.sh
7
+ include dependencies.txt
6
8
  recursive-exclude * __pycache__
7
9
  recursive-exclude * *.py[co]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codex-agent-framework
3
- Version: 0.1.13
3
+ Version: 0.1.15
4
4
  Summary: A lightweight event-driven Codex agent runtime.
5
5
  Author: Baptiste
6
6
  License-Expression: MIT
@@ -74,7 +74,9 @@ It provides a reusable `Agent` abstraction with persistent sessions, local tools
74
74
  - Python 3.10 or newer.
75
75
  - A working model backend configuration compatible with `codex-backend-sdk` / OpenAI usage.
76
76
  - GTK 3 plus Ayatana AppIndicator or AppIndicator bindings are needed for the tray controller on Linux.
77
- - Playwright's managed Chromium browser is used for rendered web extraction when static fetching is insufficient. Install it with `python -m playwright install chromium` if needed.
77
+ - Desktop automation on Linux/X11 uses explicit system tools: `wmctrl`, `xdotool`, and `xclip`.
78
+ - Playwright's managed Chromium browser is used for rendered web extraction when static fetching is insufficient.
79
+ - See `dependencies.txt` for the full list of optional non-Python system dependencies.
78
80
 
79
81
  ## Installation
80
82
 
@@ -90,6 +92,26 @@ For development:
90
92
  python -m pip install -e '.[dev]'
91
93
  ```
92
94
 
95
+ Install optional Linux system dependencies for browser, desktop, tray, service, terminal, and audio features:
96
+
97
+ ```bash
98
+ codex-agent install-system-deps -- -y
99
+ ```
100
+
101
+ Or bootstrap a local desktop setup by installing system dependencies and then installing/starting the user services:
102
+
103
+ ```bash
104
+ codex-agent bootstrap -- -y
105
+ ```
106
+
107
+ Useful bootstrap variants:
108
+
109
+ ```bash
110
+ codex-agent bootstrap --no-system-deps
111
+ codex-agent bootstrap --no-start-service -- -y
112
+ codex-agent install-system-deps -- --dry-run --no-tray --no-audio
113
+ ```
114
+
93
115
  ## Quick start
94
116
 
95
117
  Run the local server plus terminal TUI:
@@ -101,19 +123,29 @@ codex-agent
101
123
  Run only the long-lived local server:
102
124
 
103
125
  ```bash
104
- codex-agent server
126
+ codex-agent start server
105
127
  ```
106
128
 
107
129
  Connect a TUI to an already running server:
108
130
 
109
131
  ```bash
110
- codex-agent chat
132
+ codex-agent open tui
111
133
  ```
112
134
 
113
135
  Start only the tray controller:
114
136
 
115
137
  ```bash
116
- codex-agent tray
138
+ codex-agent start tray
139
+ ```
140
+
141
+ Headless runtime examples:
142
+
143
+ ```bash
144
+ codex-agent status --json
145
+ codex-agent tools
146
+ codex-agent config get input_token_limit
147
+ codex-agent config set input_token_limit 128000
148
+ codex-agent run "summarize this repository"
117
149
  ```
118
150
 
119
151
  Install the user services for the server and tray controller:
@@ -122,6 +154,8 @@ Install the user services for the server and tray controller:
122
154
  codex-agent install-service
123
155
  ```
124
156
 
157
+ For a first-time Linux desktop setup, `codex-agent bootstrap -- -y` combines optional system dependency installation with service installation/startup. The command forwards arguments after `--` to the system dependency installer.
158
+
125
159
  Or use the agent from Python:
126
160
 
127
161
  ```python
@@ -235,6 +269,7 @@ Key endpoints:
235
269
  GET /health
236
270
  GET /status
237
271
  GET /config
272
+ PATCH /config
238
273
  GET /session
239
274
  GET /sessions
240
275
  GET /messages
@@ -356,13 +391,15 @@ agent = Agent(
356
391
  )
357
392
  ```
358
393
 
359
- Configuration is persisted to `agent_config.json` in the runtime directory when updated through agent helpers or slash commands.
394
+ Configuration is persisted to `agent_config.json` in the runtime directory when updated through agent helpers, slash commands, the HTTP API, or `codex-agent config set`.
360
395
 
361
396
  ## Project layout
362
397
 
363
398
  ```text
364
399
  codex_agent/ Python package
365
400
  codex_agent/agent.py Agent, AgentConfig, AgentSession
401
+ codex_agent/agent_runtime.py Runtime abstraction for server, CLI, and process adapters
402
+ codex_agent/cli/ Command line interface package
366
403
  codex_agent/plugin.py Stateful plugin base class
367
404
  codex_agent/builtin_tools/ Built-in local tools package
368
405
  codex_agent/builtin_commands.py Built-in slash commands
@@ -388,7 +425,7 @@ The tests isolate `AGENT_RUNTIME_DIR` automatically, so they should not create o
388
425
  Current baseline:
389
426
 
390
427
  ```text
391
- 376 passed
428
+ 82 targeted release-regression tests passed
392
429
  ```
393
430
 
394
431
  ## Packaging
@@ -400,9 +437,11 @@ python -m pip install build
400
437
  python -m build
401
438
  ```
402
439
 
403
- The distribution includes prompt text files and `codex_agent/get_text/default_gitignore` through package data and `MANIFEST.in`.
440
+ The distribution includes prompt text files, `codex_agent/get_text/default_gitignore`, and the packaged Linux system dependency installer through package data and `MANIFEST.in`.
404
441
 
405
442
  ## Recent changes
443
+ - `0.1.15`: add the `AgentRuntime` interface for server/CLI/process adapters, split the CLI into a package with headless runtime commands, add `codex-agent config set` plus `PATCH /config`, and make built-in tools/providers/plugins configurable.
444
+ - `0.1.14`: add HookManager infrastructure, planner/scheduler robustness fixes, documented system dependencies, `codex-agent install-system-deps`, `codex-agent bootstrap`, and improved TUI SSE reconnect/replay handling.
406
445
  - `0.1.13`: reorganize built-in tools into a `codex_agent.builtin_tools` package, keeping the public import surface compatible while separating file, shell, vision, system, and server-tool modules.
407
446
  - `0.1.12`: add a persistent Playwright/Chromium browser controller with tab navigation, DOM/action snapshots, screenshots, form/click/key tools, and `browser_goto(url)` for active-tab navigation.
408
447
  - `0.1.11`: split strict line-numbered UTF-8 `read` from unnumbered extracted `view`, preserve blank lines in read snippets, and show persistent+temporary message counts in the TUI status bar.
@@ -25,7 +25,9 @@ It provides a reusable `Agent` abstraction with persistent sessions, local tools
25
25
  - Python 3.10 or newer.
26
26
  - A working model backend configuration compatible with `codex-backend-sdk` / OpenAI usage.
27
27
  - GTK 3 plus Ayatana AppIndicator or AppIndicator bindings are needed for the tray controller on Linux.
28
- - Playwright's managed Chromium browser is used for rendered web extraction when static fetching is insufficient. Install it with `python -m playwright install chromium` if needed.
28
+ - Desktop automation on Linux/X11 uses explicit system tools: `wmctrl`, `xdotool`, and `xclip`.
29
+ - Playwright's managed Chromium browser is used for rendered web extraction when static fetching is insufficient.
30
+ - See `dependencies.txt` for the full list of optional non-Python system dependencies.
29
31
 
30
32
  ## Installation
31
33
 
@@ -41,6 +43,26 @@ For development:
41
43
  python -m pip install -e '.[dev]'
42
44
  ```
43
45
 
46
+ Install optional Linux system dependencies for browser, desktop, tray, service, terminal, and audio features:
47
+
48
+ ```bash
49
+ codex-agent install-system-deps -- -y
50
+ ```
51
+
52
+ Or bootstrap a local desktop setup by installing system dependencies and then installing/starting the user services:
53
+
54
+ ```bash
55
+ codex-agent bootstrap -- -y
56
+ ```
57
+
58
+ Useful bootstrap variants:
59
+
60
+ ```bash
61
+ codex-agent bootstrap --no-system-deps
62
+ codex-agent bootstrap --no-start-service -- -y
63
+ codex-agent install-system-deps -- --dry-run --no-tray --no-audio
64
+ ```
65
+
44
66
  ## Quick start
45
67
 
46
68
  Run the local server plus terminal TUI:
@@ -52,19 +74,29 @@ codex-agent
52
74
  Run only the long-lived local server:
53
75
 
54
76
  ```bash
55
- codex-agent server
77
+ codex-agent start server
56
78
  ```
57
79
 
58
80
  Connect a TUI to an already running server:
59
81
 
60
82
  ```bash
61
- codex-agent chat
83
+ codex-agent open tui
62
84
  ```
63
85
 
64
86
  Start only the tray controller:
65
87
 
66
88
  ```bash
67
- codex-agent tray
89
+ codex-agent start tray
90
+ ```
91
+
92
+ Headless runtime examples:
93
+
94
+ ```bash
95
+ codex-agent status --json
96
+ codex-agent tools
97
+ codex-agent config get input_token_limit
98
+ codex-agent config set input_token_limit 128000
99
+ codex-agent run "summarize this repository"
68
100
  ```
69
101
 
70
102
  Install the user services for the server and tray controller:
@@ -73,6 +105,8 @@ Install the user services for the server and tray controller:
73
105
  codex-agent install-service
74
106
  ```
75
107
 
108
+ For a first-time Linux desktop setup, `codex-agent bootstrap -- -y` combines optional system dependency installation with service installation/startup. The command forwards arguments after `--` to the system dependency installer.
109
+
76
110
  Or use the agent from Python:
77
111
 
78
112
  ```python
@@ -186,6 +220,7 @@ Key endpoints:
186
220
  GET /health
187
221
  GET /status
188
222
  GET /config
223
+ PATCH /config
189
224
  GET /session
190
225
  GET /sessions
191
226
  GET /messages
@@ -307,13 +342,15 @@ agent = Agent(
307
342
  )
308
343
  ```
309
344
 
310
- Configuration is persisted to `agent_config.json` in the runtime directory when updated through agent helpers or slash commands.
345
+ Configuration is persisted to `agent_config.json` in the runtime directory when updated through agent helpers, slash commands, the HTTP API, or `codex-agent config set`.
311
346
 
312
347
  ## Project layout
313
348
 
314
349
  ```text
315
350
  codex_agent/ Python package
316
351
  codex_agent/agent.py Agent, AgentConfig, AgentSession
352
+ codex_agent/agent_runtime.py Runtime abstraction for server, CLI, and process adapters
353
+ codex_agent/cli/ Command line interface package
317
354
  codex_agent/plugin.py Stateful plugin base class
318
355
  codex_agent/builtin_tools/ Built-in local tools package
319
356
  codex_agent/builtin_commands.py Built-in slash commands
@@ -339,7 +376,7 @@ The tests isolate `AGENT_RUNTIME_DIR` automatically, so they should not create o
339
376
  Current baseline:
340
377
 
341
378
  ```text
342
- 376 passed
379
+ 82 targeted release-regression tests passed
343
380
  ```
344
381
 
345
382
  ## Packaging
@@ -351,9 +388,11 @@ python -m pip install build
351
388
  python -m build
352
389
  ```
353
390
 
354
- The distribution includes prompt text files and `codex_agent/get_text/default_gitignore` through package data and `MANIFEST.in`.
391
+ The distribution includes prompt text files, `codex_agent/get_text/default_gitignore`, and the packaged Linux system dependency installer through package data and `MANIFEST.in`.
355
392
 
356
393
  ## Recent changes
394
+ - `0.1.15`: add the `AgentRuntime` interface for server/CLI/process adapters, split the CLI into a package with headless runtime commands, add `codex-agent config set` plus `PATCH /config`, and make built-in tools/providers/plugins configurable.
395
+ - `0.1.14`: add HookManager infrastructure, planner/scheduler robustness fixes, documented system dependencies, `codex-agent install-system-deps`, `codex-agent bootstrap`, and improved TUI SSE reconnect/replay handling.
357
396
  - `0.1.13`: reorganize built-in tools into a `codex_agent.builtin_tools` package, keeping the public import surface compatible while separating file, shell, vision, system, and server-tool modules.
358
397
  - `0.1.12`: add a persistent Playwright/Chromium browser controller with tab navigation, DOM/action snapshots, screenshots, form/click/key tools, and `browser_goto(url)` for active-tab navigation.
359
398
  - `0.1.11`: split strict line-numbered UTF-8 `read` from unnumbered extracted `view`, preserve blank lines in read snippets, and show persistent+temporary message counts in the TUI status bar.
@@ -37,6 +37,7 @@ from .message import (
37
37
  AssistantMessage,
38
38
  CompactionMessage,
39
39
  DeveloperMessage,
40
+ InterruptMessage,
40
41
  Message,
41
42
  MessageChunk,
42
43
  ProviderMessage,
@@ -98,6 +99,7 @@ __all__ = [
98
99
  "AssistantMessage",
99
100
  "CompactionMessage",
100
101
  "DeveloperMessage",
102
+ "InterruptMessage",
101
103
  "Message",
102
104
  "MessageChunk",
103
105
  "ProviderMessage",
@@ -0,0 +1,7 @@
1
+ """Compatibility entrypoint for ``python -m codex_agent``."""
2
+
3
+ from .cli.main import main
4
+
5
+
6
+ if __name__ == '__main__':
7
+ main()
@@ -7,6 +7,7 @@ from .image import ImageMessage
7
7
  from .command import CommandManager
8
8
  from .config import ConfigManager
9
9
  from .context import ContextManager
10
+ from .hooks import HookManager
10
11
  from .mainloop import MainLoopManager
11
12
  from .plugin import PluginsManager
12
13
  from .registry import RegistryManager
@@ -14,7 +15,7 @@ from .runtime import RuntimeManager
14
15
  from .sessions import SessionsManager
15
16
  from .status import StatusManager
16
17
  from .tool import ImageGenerationTool, reset_current_agent, set_current_agent
17
- from .ai import AIClient
18
+ from .ai import AIBadRequestError, AIClient
18
19
  from .voice import VoiceProcessor
19
20
  from .latex import LaTeXProcessor
20
21
  from .event import AgentInterruptRequestedEvent, AgentInterrupted, AgentInterruptedEvent, AudioPlaybackEvent, EventBus
@@ -49,7 +50,7 @@ class Agent:
49
50
  self.plugins=self.plugins_manager.plugins
50
51
  self.sessions_manager=SessionsManager(self)
51
52
  self.runtime=RuntimeManager(self)
52
- self.hooks=modict()
53
+ self.hooks=HookManager(self)
53
54
  self.on(AudioPlaybackEvent, self.handle_audio_playback)
54
55
  self.mainloop_manager=MainLoopManager(self)
55
56
  self._interrupt_requested=ThreadEvent()
@@ -126,7 +127,7 @@ class Agent:
126
127
  self.emit(AgentInterruptedEvent, reason=reason)
127
128
  self.emit(AgentInterruptRequestedEvent, reason=reason, interrupted=True, error=str(abort_error) if abort_error else "")
128
129
  return True
129
-
130
+
130
131
  def clear_interrupt(self):
131
132
  self._interrupt_requested.clear()
132
133
 
@@ -189,7 +190,6 @@ class Agent:
189
190
  if self.mainloop_manager.dumping_pending:
190
191
  return self.sessions_manager.append_message(msg)
191
192
  return self.mainloop_manager.add_pending_message(msg)
192
-
193
193
 
194
194
  def new_message(self,pending=False,**kwargs):
195
195
  """Helper to create and add a message based on provided keyword arguments.
@@ -203,7 +203,7 @@ class Agent:
203
203
  """
204
204
  msg=message_from_data(kwargs)
205
205
  return self.add_message(msg,pending=pending)
206
-
206
+
207
207
  def add_image(self,source,pending=False,**kwargs):
208
208
  if source:
209
209
  img=ImageMessage(
@@ -247,21 +247,24 @@ class Agent:
247
247
  if hasattr(self, "mainloop_manager") and self.mainloop_manager.should_queue_state_command():
248
248
  return self.submit("add_hook", key=key, func=func).wait()
249
249
 
250
- self.hooks.setdefault(key, []).append(func)
251
- return func
250
+ return self.hooks.add(key, func)
252
251
 
253
252
  def has_hook(self, key):
254
- return bool(self.hooks.get(key))
253
+ return self.hooks.has(key)
254
+
255
+ def run_hook(self, key, **payload):
256
+ agent_context = set_current_agent(self)
257
+ try:
258
+ return self.hooks.run(key, **payload)
259
+ finally:
260
+ reset_current_agent(agent_context)
255
261
 
256
262
  def run_hooks(self, key, *args):
257
- results = []
258
- for hook in list(self.hooks.get(key, ())):
259
- agent_context = set_current_agent(self)
260
- try:
261
- results.append(hook(*args))
262
- finally:
263
- reset_current_agent(agent_context)
264
- return results
263
+ agent_context = set_current_agent(self)
264
+ try:
265
+ return self.hooks.run_legacy(key, *args)
266
+ finally:
267
+ reset_current_agent(agent_context)
265
268
 
266
269
  def get_messages(self,filter=None):
267
270
  return self.sessions_manager.messages(filter=filter)
@@ -321,16 +324,21 @@ class Agent:
321
324
  )
322
325
  messages = self.context_manager.get_context()
323
326
 
324
- message=self.ai.run(
325
- messages=messages,
326
- tools=self.registry.get_response_tools(),
327
- prompt_cache_key=self.current_session_id,
328
- **self.config.extract('model','reasoning_effort','verbosity','parallel_tool_calls')
329
- )
327
+ try:
328
+ message=self.ai.run(
329
+ messages=messages,
330
+ tools=self.registry.get_response_tools(),
331
+ prompt_cache_key=self.current_session_id,
332
+ **self.config.extract('model','reasoning_effort','verbosity','parallel_tool_calls')
333
+ )
334
+ except AIBadRequestError:
335
+ self.sessions_manager.restore_api_success_backup()
336
+ raise
330
337
  self.sessions_manager.append_message(message)
331
338
  for item in message.output_items:
332
339
  if self.registry.server_tool_for_item(item):
333
340
  self.sessions_manager.append_message(ServerToolResponseMessage(item=item))
341
+ self.sessions_manager.save_api_success_backup()
334
342
  return message
335
343
 
336
344
  def speak(self,text,**kwargs):
@@ -381,7 +389,7 @@ class Agent:
381
389
  """
382
390
  if source is None:
383
391
  raise ValueError("An audio source must be provided")
384
-
392
+
385
393
  # Transcribe audio to text
386
394
  transcribed_text = self.ai.audio_to_text(
387
395
  source=source,
@@ -3,12 +3,13 @@ import multiprocessing
3
3
  import queue
4
4
  import threading
5
5
  import uuid
6
+ from abc import ABC, abstractmethod
6
7
 
7
8
  from modict import modict
8
9
 
9
10
  from .agent import Agent
10
11
  from .event import Event
11
- from .message import message_from_data
12
+ from .message import UserMessage, message_from_data
12
13
 
13
14
 
14
15
  def jsonable(value):
@@ -19,7 +20,143 @@ class AgentRuntimeError(RuntimeError):
19
20
  pass
20
21
 
21
22
 
22
- class InProcessAgentRuntime:
23
+ class AgentRuntime(ABC):
24
+ """Canonical contract for exposing an agent to external applications.
25
+
26
+ This ABC is the source of truth for every public surface that drives or
27
+ introspects an Agent from outside the core object: HTTP server, headless CLI,
28
+ local process adapters, remote clients and future integrations should all
29
+ implement or target this runtime interface instead of depending on Agent
30
+ internals directly.
31
+ """
32
+
33
+ @abstractmethod
34
+ def start(self):
35
+ return self
36
+
37
+ @abstractmethod
38
+ def stop(self, timeout=None):
39
+ return self
40
+
41
+ @abstractmethod
42
+ def on_event(self, handler):
43
+ return handler
44
+
45
+ @property
46
+ @abstractmethod
47
+ def busy(self):
48
+ return False
49
+
50
+ @property
51
+ @abstractmethod
52
+ def current_turn_id(self):
53
+ return None
54
+
55
+ @property
56
+ @abstractmethod
57
+ def current_session_id(self):
58
+ return None
59
+
60
+ @abstractmethod
61
+ def get_status(self):
62
+ raise NotImplementedError
63
+
64
+ @abstractmethod
65
+ def config(self):
66
+ raise NotImplementedError
67
+
68
+ @abstractmethod
69
+ def update_config(self, values=None, save=True, **kwargs):
70
+ raise NotImplementedError
71
+
72
+ @abstractmethod
73
+ def session(self):
74
+ raise NotImplementedError
75
+
76
+ @abstractmethod
77
+ def sessions(self):
78
+ raise NotImplementedError
79
+
80
+ @abstractmethod
81
+ def messages(self):
82
+ raise NotImplementedError
83
+
84
+ @abstractmethod
85
+ def memory(self):
86
+ raise NotImplementedError
87
+
88
+ @abstractmethod
89
+ def tools(self):
90
+ raise NotImplementedError
91
+
92
+ @abstractmethod
93
+ def wakeups(self, include_done=True):
94
+ raise NotImplementedError
95
+
96
+ @abstractmethod
97
+ def start_new_session(self):
98
+ raise NotImplementedError
99
+
100
+ @abstractmethod
101
+ def load_session(self, session_id):
102
+ raise NotImplementedError
103
+
104
+ @abstractmethod
105
+ def delete_session(self, session_id):
106
+ raise NotImplementedError
107
+
108
+ @abstractmethod
109
+ def navigate_session(self, direction):
110
+ raise NotImplementedError
111
+
112
+ @abstractmethod
113
+ def interrupt(self, reason="api"):
114
+ raise NotImplementedError
115
+
116
+ @abstractmethod
117
+ def start_turn(self):
118
+ raise NotImplementedError
119
+
120
+ @abstractmethod
121
+ def submit_command(self, prompt):
122
+ raise NotImplementedError
123
+
124
+ @abstractmethod
125
+ def submit_message(self, message):
126
+ raise NotImplementedError
127
+
128
+ def submit_prompt(self, prompt):
129
+ if not prompt or not str(prompt).strip():
130
+ raise ValueError("prompt must not be empty")
131
+ if str(prompt).strip().startswith("/"):
132
+ if self.busy:
133
+ raise RuntimeError("agent is busy")
134
+ turn_id = self.submit_command(prompt)
135
+ if turn_id is None:
136
+ raise RuntimeError("agent is busy")
137
+ return modict(command=True, queued=False, turn_id=turn_id)
138
+ was_busy = self.busy
139
+ result = self.submit_message(UserMessage(content=prompt))
140
+ return modict(
141
+ queued=was_busy,
142
+ pending_message_id=result.message.id,
143
+ turn_id=result.get("turn_id") or self.current_turn_id,
144
+ )
145
+
146
+ @abstractmethod
147
+ def schedule_wakeup(self, **kwargs):
148
+ raise NotImplementedError
149
+
150
+ @abstractmethod
151
+ def cancel_wakeup(self, job_id):
152
+ raise NotImplementedError
153
+
154
+ @abstractmethod
155
+ def delete_wakeup(self, job_id):
156
+ raise NotImplementedError
157
+
158
+
159
+ class InProcessAgentRuntime(AgentRuntime):
23
160
  def __init__(self, agent: Agent):
24
161
  self.agent = agent
25
162
 
@@ -53,6 +190,9 @@ class InProcessAgentRuntime:
53
190
  def config(self):
54
191
  return self.agent.config
55
192
 
193
+ def update_config(self, values=None, save=True, **kwargs):
194
+ return self.agent.config_manager.update(values, save=save, **kwargs)
195
+
56
196
  def session(self):
57
197
  return self.agent.session
58
198
 
@@ -115,7 +255,7 @@ class InProcessAgentRuntime:
115
255
  return job
116
256
 
117
257
 
118
- class ProcessAgentRuntime:
258
+ class ProcessAgentRuntime(AgentRuntime):
119
259
  def __init__(self, agent_kwargs=None, *, command_timeout=5.0, status_timeout=0.5):
120
260
  self.agent_kwargs = dict(agent_kwargs or {})
121
261
  self.command_timeout = float(command_timeout)
@@ -294,6 +434,13 @@ class ProcessAgentRuntime:
294
434
  def config(self):
295
435
  return self._cached_call("config")
296
436
 
437
+ def update_config(self, values=None, save=True, **kwargs):
438
+ payload = dict(values or {})
439
+ payload.update(kwargs)
440
+ result = self.call("update_config", {"values": payload, "save": save}, timeout=self.command_timeout)
441
+ self.cache.config = result
442
+ return result
443
+
297
444
  def session(self):
298
445
  return self._cached_call("session")
299
446
 
@@ -452,6 +599,8 @@ def run_worker_command(agent, name, payload):
452
599
  return status
453
600
  if name == "config":
454
601
  return agent.config
602
+ if name == "update_config":
603
+ return agent.config_manager.update(payload.get("values") or {}, save=payload.get("save", True))
455
604
  if name == "session":
456
605
  return agent.session
457
606
  if name == "sessions":