codex-agent-framework 0.1.14__tar.gz → 0.1.16__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 (103) hide show
  1. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/CHANGELOG.md +23 -0
  2. {codex_agent_framework-0.1.14/codex_agent_framework.egg-info → codex_agent_framework-0.1.16}/PKG-INFO +23 -6
  3. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/README.md +22 -5
  4. codex_agent_framework-0.1.16/codex_agent/__main__.py +7 -0
  5. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/codex_agent/agent.py +4 -1
  6. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/codex_agent/agent_runtime.py +152 -3
  7. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/codex_agent/ai.py +60 -9
  8. codex_agent_framework-0.1.16/codex_agent/cli/__init__.py +1 -0
  9. codex_agent_framework-0.1.16/codex_agent/cli/headless.py +203 -0
  10. codex_agent_framework-0.1.14/codex_agent/__main__.py → codex_agent_framework-0.1.16/codex_agent/cli/main.py +46 -20
  11. codex_agent_framework-0.1.16/codex_agent/cli/output.py +31 -0
  12. codex_agent_framework-0.1.16/codex_agent/cli/root.py +169 -0
  13. codex_agent_framework-0.1.16/codex_agent/cli/runner.py +87 -0
  14. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/codex_agent/client.py +20 -0
  15. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/codex_agent/config.py +28 -0
  16. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/codex_agent/mainloop.py +1 -1
  17. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/codex_agent/memory.py +13 -2
  18. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/codex_agent/plugin.py +4 -0
  19. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/codex_agent/registry.py +16 -6
  20. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/codex_agent/server.py +20 -26
  21. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/codex_agent/service.py +4 -3
  22. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16/codex_agent_framework.egg-info}/PKG-INFO +23 -6
  23. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/codex_agent_framework.egg-info/SOURCES.txt +10 -0
  24. codex_agent_framework-0.1.16/codex_agent_framework.egg-info/entry_points.txt +2 -0
  25. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/pyproject.toml +2 -2
  26. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/tests/test_agent.py +23 -0
  27. codex_agent_framework-0.1.16/tests/test_agent_runtime.py +121 -0
  28. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/tests/test_ai.py +49 -1
  29. codex_agent_framework-0.1.16/tests/test_builtin_config.py +33 -0
  30. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/tests/test_cli.py +28 -46
  31. codex_agent_framework-0.1.16/tests/test_cli_headless.py +136 -0
  32. codex_agent_framework-0.1.16/tests/test_cli_root.py +106 -0
  33. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/tests/test_memory.py +75 -0
  34. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/tests/test_server.py +58 -47
  35. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/tests/test_service.py +4 -2
  36. codex_agent_framework-0.1.14/codex_agent_framework.egg-info/entry_points.txt +0 -2
  37. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/LICENSE +0 -0
  38. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/MANIFEST.in +0 -0
  39. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/codex_agent/__init__.py +0 -0
  40. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/codex_agent/builtin_commands.py +0 -0
  41. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/codex_agent/builtin_plugins/__init__.py +0 -0
  42. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/codex_agent/builtin_plugins/browser/__init__.py +0 -0
  43. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/codex_agent/builtin_plugins/browser/controller.py +0 -0
  44. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/codex_agent/builtin_plugins/desktop/__init__.py +0 -0
  45. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/codex_agent/builtin_plugins/desktop/controller.py +0 -0
  46. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/codex_agent/builtin_plugins/desktop/screenshot.py +0 -0
  47. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/codex_agent/builtin_plugins/memory/__init__.py +0 -0
  48. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/codex_agent/builtin_plugins/planner/__init__.py +0 -0
  49. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/codex_agent/builtin_plugins/scheduler/__init__.py +0 -0
  50. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/codex_agent/builtin_providers.py +0 -0
  51. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/codex_agent/builtin_tools/__init__.py +0 -0
  52. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/codex_agent/builtin_tools/files.py +0 -0
  53. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/codex_agent/builtin_tools/server_tools.py +0 -0
  54. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/codex_agent/builtin_tools/shell.py +0 -0
  55. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/codex_agent/builtin_tools/system.py +0 -0
  56. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/codex_agent/builtin_tools/vision.py +0 -0
  57. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/codex_agent/chat.py +0 -0
  58. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/codex_agent/command.py +0 -0
  59. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/codex_agent/context.py +0 -0
  60. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/codex_agent/event.py +0 -0
  61. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/codex_agent/get_text/__init__.py +0 -0
  62. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/codex_agent/get_text/default_gitignore +0 -0
  63. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/codex_agent/get_text/get_text.py +0 -0
  64. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/codex_agent/get_text/simpler_get_text.py +0 -0
  65. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/codex_agent/hooks.py +0 -0
  66. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/codex_agent/image.py +0 -0
  67. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/codex_agent/latex.py +0 -0
  68. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/codex_agent/message.py +0 -0
  69. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/codex_agent/prompts/image_generation_system_prompt.txt +0 -0
  70. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/codex_agent/prompts/system_prompt.txt +0 -0
  71. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/codex_agent/provider.py +0 -0
  72. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/codex_agent/runtime.py +0 -0
  73. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/codex_agent/scheduler.py +0 -0
  74. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/codex_agent/scripts/install-system-dependencies.sh +0 -0
  75. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/codex_agent/sessions.py +0 -0
  76. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/codex_agent/status.py +0 -0
  77. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/codex_agent/stream_utils.py +0 -0
  78. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/codex_agent/tool.py +0 -0
  79. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/codex_agent/tray.py +0 -0
  80. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/codex_agent/tui.py +0 -0
  81. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/codex_agent/utils.py +0 -0
  82. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/codex_agent/voice.py +0 -0
  83. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/codex_agent/worker.py +0 -0
  84. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/codex_agent_framework.egg-info/dependency_links.txt +0 -0
  85. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/codex_agent_framework.egg-info/requires.txt +0 -0
  86. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/codex_agent_framework.egg-info/top_level.txt +0 -0
  87. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/dependencies.txt +0 -0
  88. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/setup.cfg +0 -0
  89. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/tests/test_browser.py +0 -0
  90. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/tests/test_chat.py +0 -0
  91. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/tests/test_client.py +0 -0
  92. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/tests/test_events.py +0 -0
  93. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/tests/test_get_text_browser.py +0 -0
  94. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/tests/test_hooks.py +0 -0
  95. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/tests/test_image_message.py +0 -0
  96. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/tests/test_local_desktop.py +0 -0
  97. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/tests/test_messages.py +0 -0
  98. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/tests/test_planner.py +0 -0
  99. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/tests/test_scheduler.py +0 -0
  100. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/tests/test_tray.py +0 -0
  101. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/tests/test_tui.py +0 -0
  102. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/tests/test_utils.py +0 -0
  103. {codex_agent_framework-0.1.14 → codex_agent_framework-0.1.16}/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.16] - 2026-05-08
8
+ ### Fixed
9
+ - Allow agent startup without an OpenAI API key by disabling OpenAI-dependent capabilities such as voice defaults and the built-in memory plugin instead of failing during initialization.
10
+ - Skip automatic memory archiving when the memory plugin is unavailable.
11
+
12
+ ### Tests
13
+ - Add regression coverage for agent initialization with and without an OpenAI API key.
14
+
15
+ ## [0.1.15] - 2026-05-08
16
+ ### Added
17
+ - Add `AgentRuntime` as the canonical external runtime contract for HTTP server, headless CLI, in-process, and process-backed integrations.
18
+ - Add structured runtime configuration updates through `PATCH /config`, `AgentClient.update_config()`, and `codex-agent config set`.
19
+ - 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.
20
+
21
+ ### Changed
22
+ - Split the command line implementation into the `codex_agent.cli` package with root orchestration commands and headless runtime commands.
23
+ - Route server operations through `AgentRuntime` instead of depending on `Agent` internals directly.
24
+ - Update CLI service/interface commands to the explicit `start/stop/restart server|tray` and `open/close tui` forms.
25
+
26
+ ### Tests
27
+ - Add regression coverage for the runtime abstraction, headless CLI, root CLI dispatch, config updates, and built-in selection.
28
+ - Validate the targeted release-regression suite at 82 passing tests.
29
+
7
30
  ## [0.1.14] - 2026-05-07
8
31
  ### Added
9
32
  - 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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codex-agent-framework
3
- Version: 0.1.14
3
+ Version: 0.1.16
4
4
  Summary: A lightweight event-driven Codex agent runtime.
5
5
  Author: Baptiste
6
6
  License-Expression: MIT
@@ -123,19 +123,29 @@ codex-agent
123
123
  Run only the long-lived local server:
124
124
 
125
125
  ```bash
126
- codex-agent server
126
+ codex-agent start server
127
127
  ```
128
128
 
129
129
  Connect a TUI to an already running server:
130
130
 
131
131
  ```bash
132
- codex-agent chat
132
+ codex-agent open tui
133
133
  ```
134
134
 
135
135
  Start only the tray controller:
136
136
 
137
137
  ```bash
138
- 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"
139
149
  ```
140
150
 
141
151
  Install the user services for the server and tray controller:
@@ -259,6 +269,7 @@ Key endpoints:
259
269
  GET /health
260
270
  GET /status
261
271
  GET /config
272
+ PATCH /config
262
273
  GET /session
263
274
  GET /sessions
264
275
  GET /messages
@@ -380,13 +391,17 @@ agent = Agent(
380
391
  )
381
392
  ```
382
393
 
383
- 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`.
395
+
396
+ If no OpenAI API key is configured through `openai_api_key` or `OPENAI_API_KEY`, the agent can still start, but OpenAI-dependent capabilities such as voice defaults and the built-in memory plugin are disabled.
384
397
 
385
398
  ## Project layout
386
399
 
387
400
  ```text
388
401
  codex_agent/ Python package
389
402
  codex_agent/agent.py Agent, AgentConfig, AgentSession
403
+ codex_agent/agent_runtime.py Runtime abstraction for server, CLI, and process adapters
404
+ codex_agent/cli/ Command line interface package
390
405
  codex_agent/plugin.py Stateful plugin base class
391
406
  codex_agent/builtin_tools/ Built-in local tools package
392
407
  codex_agent/builtin_commands.py Built-in slash commands
@@ -412,7 +427,7 @@ The tests isolate `AGENT_RUNTIME_DIR` automatically, so they should not create o
412
427
  Current baseline:
413
428
 
414
429
  ```text
415
- 412 passed
430
+ 82 targeted release-regression tests passed
416
431
  ```
417
432
 
418
433
  ## Packaging
@@ -427,6 +442,8 @@ python -m build
427
442
  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`.
428
443
 
429
444
  ## Recent changes
445
+ - `0.1.16`: tolerate missing OpenAI API keys at startup by disabling OpenAI-dependent voice and memory features, and skip memory archiving when the memory plugin is unavailable.
446
+ - `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.
430
447
  - `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.
431
448
  - `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.
432
449
  - `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.
@@ -74,19 +74,29 @@ codex-agent
74
74
  Run only the long-lived local server:
75
75
 
76
76
  ```bash
77
- codex-agent server
77
+ codex-agent start server
78
78
  ```
79
79
 
80
80
  Connect a TUI to an already running server:
81
81
 
82
82
  ```bash
83
- codex-agent chat
83
+ codex-agent open tui
84
84
  ```
85
85
 
86
86
  Start only the tray controller:
87
87
 
88
88
  ```bash
89
- 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"
90
100
  ```
91
101
 
92
102
  Install the user services for the server and tray controller:
@@ -210,6 +220,7 @@ Key endpoints:
210
220
  GET /health
211
221
  GET /status
212
222
  GET /config
223
+ PATCH /config
213
224
  GET /session
214
225
  GET /sessions
215
226
  GET /messages
@@ -331,13 +342,17 @@ agent = Agent(
331
342
  )
332
343
  ```
333
344
 
334
- 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`.
346
+
347
+ If no OpenAI API key is configured through `openai_api_key` or `OPENAI_API_KEY`, the agent can still start, but OpenAI-dependent capabilities such as voice defaults and the built-in memory plugin are disabled.
335
348
 
336
349
  ## Project layout
337
350
 
338
351
  ```text
339
352
  codex_agent/ Python package
340
353
  codex_agent/agent.py Agent, AgentConfig, AgentSession
354
+ codex_agent/agent_runtime.py Runtime abstraction for server, CLI, and process adapters
355
+ codex_agent/cli/ Command line interface package
341
356
  codex_agent/plugin.py Stateful plugin base class
342
357
  codex_agent/builtin_tools/ Built-in local tools package
343
358
  codex_agent/builtin_commands.py Built-in slash commands
@@ -363,7 +378,7 @@ The tests isolate `AGENT_RUNTIME_DIR` automatically, so they should not create o
363
378
  Current baseline:
364
379
 
365
380
  ```text
366
- 412 passed
381
+ 82 targeted release-regression tests passed
367
382
  ```
368
383
 
369
384
  ## Packaging
@@ -378,6 +393,8 @@ python -m build
378
393
  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`.
379
394
 
380
395
  ## Recent changes
396
+ - `0.1.16`: tolerate missing OpenAI API keys at startup by disabling OpenAI-dependent voice and memory features, and skip memory archiving when the memory plugin is unavailable.
397
+ - `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.
381
398
  - `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.
382
399
  - `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.
383
400
  - `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.
@@ -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()
@@ -1,4 +1,4 @@
1
- from .utils import text_content, ensure_runtime_dir, upload_file_to_folder, project_python_env
1
+ from .utils import text_content, ensure_runtime_dir, upload_file_to_folder, project_python_env, runtime_join
2
2
  from modict import modict
3
3
  import os
4
4
  import sys
@@ -17,6 +17,7 @@ from .status import StatusManager
17
17
  from .tool import ImageGenerationTool, reset_current_agent, set_current_agent
18
18
  from .ai import AIBadRequestError, AIClient
19
19
  from .voice import VoiceProcessor
20
+ from .memory import RAGMemory
20
21
  from .latex import LaTeXProcessor
21
22
  from .event import AgentInterruptRequestedEvent, AgentInterrupted, AgentInterruptedEvent, AudioPlaybackEvent, EventBus
22
23
  from pynteract import Shell
@@ -39,6 +40,7 @@ class Agent:
39
40
  self.config=self.config_manager.config
40
41
  self.config_manager.load()
41
42
  self.config_manager.update(kwargs, save=False)
43
+ self.config_manager.apply_openai_capability_defaults()
42
44
  self.events=EventBus()
43
45
  self.registry=RegistryManager(self)
44
46
  self.tools=self.registry.tools
@@ -65,6 +67,7 @@ class Agent:
65
67
  self.registry.init_providers_folder()
66
68
  self.command_manager.init_folder()
67
69
  self.ai=AIClient(self)
70
+ self.memory=RAGMemory(file=runtime_join("memory.json"))
68
71
  self.voice=VoiceProcessor(self)
69
72
  self.latex=LaTeXProcessor()
70
73
  self.content_processors=[self.voice]
@@ -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":
@@ -456,6 +456,8 @@ class AIClient:
456
456
  if not isinstance(content, str):
457
457
  raise ValueError(f"Content must be a string, got {type(content)}")
458
458
  content = self.truncate_embedding_input(content, model=model)
459
+ if not content.strip():
460
+ raise ValueError("Content must not be empty")
459
461
 
460
462
  try:
461
463
  response = self.client.embeddings.create(
@@ -485,9 +487,13 @@ class AIClient:
485
487
  Note:
486
488
  OpenAI API handles batching automatically for better performance.
487
489
  """
490
+ if not contents:
491
+ return []
488
492
  if not all(isinstance(content, str) for content in contents):
489
493
  raise ValueError("All contents must be strings")
490
494
  contents = [self.truncate_embedding_input(content) for content in contents]
495
+ if any(not content.strip() for content in contents):
496
+ raise ValueError("Embedding contents must not contain empty strings")
491
497
 
492
498
  try:
493
499
  response = self.client.embeddings.create(
@@ -501,6 +507,14 @@ class AIClient:
501
507
 
502
508
  return embeddings
503
509
 
510
+ def message_embedding_content(self, msg):
511
+ if hasattr(msg, 'content') and isinstance(msg.content, str):
512
+ return msg.content.strip()
513
+ raise ValueError("Message must have text content")
514
+
515
+ def zero_embedding(self, dimensions=128):
516
+ return [0.0] * int(dimensions)
517
+
504
518
  def similarity(self, emb1, emb2):
505
519
  """
506
520
  Calculate shifted positive cosine similarity between two embeddings.
@@ -524,15 +538,52 @@ class AIClient:
524
538
  Note:
525
539
  Only text content is supported.
526
540
  """
527
- if hasattr(msg, 'content') and isinstance(msg.content, str):
528
- content = msg.content
529
- else:
530
- raise ValueError("Message must have text content")
531
-
532
- # Generate embedding
541
+ content = self.message_embedding_content(msg)
542
+ if not content:
543
+ embedding = self.zero_embedding(dimensions)
544
+ msg.embedding = embedding
545
+ return embedding
533
546
  embedding = self.embed_content(content, precision, dimensions, model)
534
-
535
- # Assign to msg.embedding
536
547
  msg.embedding = embedding
537
-
538
548
  return embedding
549
+
550
+ def embed_messages(self, messages, precision=5, dimensions=128, model="text-embedding-3-small"):
551
+ """
552
+ Embed multiple Message objects in one embeddings API request and assign
553
+ the resulting vectors to each message.
554
+ """
555
+ messages = list(messages)
556
+ if not messages:
557
+ return []
558
+
559
+ embeddings = [None] * len(messages)
560
+ batch = []
561
+ batch_indices = []
562
+ for index, msg in enumerate(messages):
563
+ content = self.message_embedding_content(msg)
564
+ if not content:
565
+ embedding = self.zero_embedding(dimensions)
566
+ msg.embedding = embedding
567
+ embeddings[index] = embedding
568
+ continue
569
+ batch.append(content)
570
+ batch_indices.append(index)
571
+
572
+ if not batch:
573
+ return embeddings
574
+
575
+ batch_embeddings = self.embed_contents(
576
+ batch,
577
+ precision=precision,
578
+ dimensions=dimensions,
579
+ model=model,
580
+ )
581
+ if len(batch_embeddings) != len(batch):
582
+ raise AIClientError(
583
+ f"Expected {len(batch)} embeddings, got {len(batch_embeddings)}"
584
+ )
585
+ for index, embedding in zip(batch_indices, batch_embeddings):
586
+ msg = messages[index]
587
+ msg.embedding = embedding
588
+ embeddings[index] = embedding
589
+ return embeddings
@@ -0,0 +1 @@
1
+ """Command-line interface package for codex-agent."""