codex-agent-framework 0.1.16__tar.gz → 0.1.17__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/CHANGELOG.md +12 -0
- {codex_agent_framework-0.1.16/codex_agent_framework.egg-info → codex_agent_framework-0.1.17}/PKG-INFO +2 -1
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/README.md +1 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/agent.py +1 -2
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/cli/main.py +1 -1
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/message.py +0 -1
- codex_agent_framework-0.1.17/codex_agent/server/__init__.py +29 -0
- codex_agent_framework-0.1.17/codex_agent/server/app.py +201 -0
- codex_agent_framework-0.1.16/codex_agent/server.py → codex_agent_framework-0.1.17/codex_agent/server/core.py +29 -208
- codex_agent_framework-0.1.17/codex_agent/tui/__init__.py +25 -0
- {codex_agent_framework-0.1.16/codex_agent → codex_agent_framework-0.1.17/codex_agent/tui}/chat.py +3 -3
- codex_agent_framework-0.1.16/codex_agent/tui.py → codex_agent_framework-0.1.17/codex_agent/tui/lifecycle.py +1 -1
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17/codex_agent_framework.egg-info}/PKG-INFO +2 -1
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent_framework.egg-info/SOURCES.txt +6 -3
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/pyproject.toml +1 -1
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/tests/test_chat.py +2 -2
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/tests/test_server.py +21 -21
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/tests/test_tui.py +1 -1
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/LICENSE +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/MANIFEST.in +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/__init__.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/__main__.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/agent_runtime.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/ai.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/builtin_commands.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/builtin_plugins/__init__.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/builtin_plugins/browser/__init__.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/builtin_plugins/browser/controller.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/builtin_plugins/desktop/__init__.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/builtin_plugins/desktop/controller.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/builtin_plugins/desktop/screenshot.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/builtin_plugins/memory/__init__.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/builtin_plugins/planner/__init__.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/builtin_plugins/scheduler/__init__.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/builtin_providers.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/builtin_tools/__init__.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/builtin_tools/files.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/builtin_tools/server_tools.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/builtin_tools/shell.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/builtin_tools/system.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/builtin_tools/vision.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/cli/__init__.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/cli/headless.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/cli/output.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/cli/root.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/cli/runner.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/client.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/command.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/config.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/context.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/event.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/get_text/__init__.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/get_text/default_gitignore +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/get_text/get_text.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/get_text/simpler_get_text.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/hooks.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/image.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/latex.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/mainloop.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/memory.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/plugin.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/prompts/image_generation_system_prompt.txt +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/prompts/system_prompt.txt +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/provider.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/registry.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/runtime.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/scheduler.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/scripts/install-system-dependencies.sh +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/service.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/sessions.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/status.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/stream_utils.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/tool.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/tray.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/utils.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/voice.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/worker.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent_framework.egg-info/dependency_links.txt +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent_framework.egg-info/entry_points.txt +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent_framework.egg-info/requires.txt +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent_framework.egg-info/top_level.txt +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/dependencies.txt +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/setup.cfg +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/tests/test_agent.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/tests/test_agent_runtime.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/tests/test_ai.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/tests/test_browser.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/tests/test_builtin_config.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/tests/test_cli.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/tests/test_cli_headless.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/tests/test_cli_root.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/tests/test_client.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/tests/test_events.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/tests/test_get_text_browser.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/tests/test_hooks.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/tests/test_image_message.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/tests/test_local_desktop.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/tests/test_memory.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/tests/test_messages.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/tests/test_planner.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/tests/test_scheduler.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/tests/test_service.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/tests/test_tray.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/tests/test_utils.py +0 -0
- {codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/tests/test_worker.py +0 -0
|
@@ -4,6 +4,18 @@ 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.17] - 2026-05-08
|
|
8
|
+
### Changed
|
|
9
|
+
- Reorganize the Textual TUI into the `codex_agent.tui` package, splitting chat UI code from TUI lifecycle helpers.
|
|
10
|
+
- Reorganize the FastAPI bridge into the `codex_agent.server` package, separating server core/service logic from app and route creation.
|
|
11
|
+
- Remove the obsolete `Message.lasting` field now that message visibility is controlled by `turns_left`, reducing serialized payload noise.
|
|
12
|
+
|
|
13
|
+
### Removed
|
|
14
|
+
- Remove the old flat `codex_agent.chat` and `codex_agent.tui` modules in favor of the new package layout.
|
|
15
|
+
|
|
16
|
+
### Tests
|
|
17
|
+
- Validate the full suite at 442 passing tests.
|
|
18
|
+
|
|
7
19
|
## [0.1.16] - 2026-05-08
|
|
8
20
|
### Fixed
|
|
9
21
|
- 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.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codex-agent-framework
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.17
|
|
4
4
|
Summary: A lightweight event-driven Codex agent runtime.
|
|
5
5
|
Author: Baptiste
|
|
6
6
|
License-Expression: MIT
|
|
@@ -442,6 +442,7 @@ python -m build
|
|
|
442
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`.
|
|
443
443
|
|
|
444
444
|
## Recent changes
|
|
445
|
+
- `0.1.17`: reorganize the TUI and FastAPI server into dedicated `codex_agent.tui` and `codex_agent.server` packages, remove obsolete message payload fields, and clean unused imports.
|
|
445
446
|
- `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
447
|
- `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.
|
|
447
448
|
- `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.
|
|
@@ -393,6 +393,7 @@ python -m build
|
|
|
393
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`.
|
|
394
394
|
|
|
395
395
|
## Recent changes
|
|
396
|
+
- `0.1.17`: reorganize the TUI and FastAPI server into dedicated `codex_agent.tui` and `codex_agent.server` packages, remove obsolete message payload fields, and clean unused imports.
|
|
396
397
|
- `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
398
|
- `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.
|
|
398
399
|
- `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.
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
from .utils import text_content, ensure_runtime_dir, upload_file_to_folder, project_python_env, runtime_join
|
|
2
|
-
from modict import modict
|
|
3
2
|
import os
|
|
4
3
|
import sys
|
|
5
4
|
from .message import DeveloperMessage, ServerToolResponseMessage, SystemMessage, ToolResultMessage, UserMessage, message_from_data
|
|
@@ -432,5 +431,5 @@ class Agent:
|
|
|
432
431
|
import os
|
|
433
432
|
if os.environ.get('AGENT_ROOT_PATH') is None:
|
|
434
433
|
set_root_path(file=__file__)
|
|
435
|
-
from .
|
|
434
|
+
from .tui import Chat
|
|
436
435
|
Chat(self).run()
|
|
@@ -2,7 +2,7 @@ from pathlib import Path
|
|
|
2
2
|
import subprocess
|
|
3
3
|
|
|
4
4
|
from codex_agent import Agent
|
|
5
|
-
from codex_agent.
|
|
5
|
+
from codex_agent.tui import Chat
|
|
6
6
|
from codex_agent.server import create_app
|
|
7
7
|
from codex_agent.service import install_user_service, uninstall_user_service
|
|
8
8
|
from codex_agent.tray import run_tray
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""FastAPI server package for codex-agent."""
|
|
2
|
+
|
|
3
|
+
from .app import app, create_app, main
|
|
4
|
+
from .core import (
|
|
5
|
+
AgentServer,
|
|
6
|
+
SERVER_CONFIG_FILE,
|
|
7
|
+
SERVER_EVENTS_FILE,
|
|
8
|
+
create_server_agent,
|
|
9
|
+
load_server_config,
|
|
10
|
+
remembered_server_session,
|
|
11
|
+
save_server_config,
|
|
12
|
+
server_config_file,
|
|
13
|
+
server_events_file,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
"AgentServer",
|
|
18
|
+
"SERVER_CONFIG_FILE",
|
|
19
|
+
"SERVER_EVENTS_FILE",
|
|
20
|
+
"app",
|
|
21
|
+
"create_app",
|
|
22
|
+
"create_server_agent",
|
|
23
|
+
"load_server_config",
|
|
24
|
+
"main",
|
|
25
|
+
"remembered_server_session",
|
|
26
|
+
"save_server_config",
|
|
27
|
+
"server_config_file",
|
|
28
|
+
"server_events_file",
|
|
29
|
+
]
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import json
|
|
2
|
+
|
|
3
|
+
from ..agent import Agent
|
|
4
|
+
from ..agent_runtime import InProcessAgentRuntime, ProcessAgentRuntime
|
|
5
|
+
from .core import AgentServer, create_server_agent, remembered_server_session
|
|
6
|
+
|
|
7
|
+
def create_app(agent: Agent | None = None, start_agent=False, process_agent=False, **agent_kwargs):
|
|
8
|
+
from contextlib import asynccontextmanager
|
|
9
|
+
|
|
10
|
+
from fastapi import FastAPI, HTTPException, Request
|
|
11
|
+
from fastapi.responses import StreamingResponse
|
|
12
|
+
from pydantic import BaseModel
|
|
13
|
+
|
|
14
|
+
if process_agent and agent is None:
|
|
15
|
+
if "session" not in agent_kwargs:
|
|
16
|
+
agent_kwargs["session"] = remembered_server_session() or "latest"
|
|
17
|
+
runtime = ProcessAgentRuntime(agent_kwargs=agent_kwargs)
|
|
18
|
+
else:
|
|
19
|
+
runtime = InProcessAgentRuntime(agent or create_server_agent(**agent_kwargs))
|
|
20
|
+
service = AgentServer(runtime=runtime)
|
|
21
|
+
if start_agent:
|
|
22
|
+
service.start()
|
|
23
|
+
|
|
24
|
+
@asynccontextmanager
|
|
25
|
+
async def lifespan(app):
|
|
26
|
+
try:
|
|
27
|
+
yield
|
|
28
|
+
finally:
|
|
29
|
+
service.stop(timeout=1.0)
|
|
30
|
+
|
|
31
|
+
app = FastAPI(title="Codex Agent", version="0.1.17", lifespan=lifespan)
|
|
32
|
+
app.state.agent_service = service
|
|
33
|
+
|
|
34
|
+
class TurnRequest(BaseModel):
|
|
35
|
+
prompt: str
|
|
36
|
+
|
|
37
|
+
class InterruptRequest(BaseModel):
|
|
38
|
+
reason: str = "api"
|
|
39
|
+
|
|
40
|
+
class ConfigUpdateRequest(BaseModel):
|
|
41
|
+
values: dict = {}
|
|
42
|
+
save: bool = True
|
|
43
|
+
|
|
44
|
+
class WakeupRequest(BaseModel):
|
|
45
|
+
prompt: str
|
|
46
|
+
run_at: str | None = None
|
|
47
|
+
delay_seconds: float | None = None
|
|
48
|
+
interval_seconds: float | None = None
|
|
49
|
+
title: str = ""
|
|
50
|
+
|
|
51
|
+
class RestartRequest(BaseModel):
|
|
52
|
+
prompt: str | None = None
|
|
53
|
+
title: str = "post-restart wakeup"
|
|
54
|
+
|
|
55
|
+
def jsonable(value):
|
|
56
|
+
return json.loads(json.dumps(value, ensure_ascii=False, default=str))
|
|
57
|
+
|
|
58
|
+
def busy_guard(call):
|
|
59
|
+
try:
|
|
60
|
+
return call()
|
|
61
|
+
except RuntimeError as exc:
|
|
62
|
+
raise HTTPException(status_code=409, detail=str(exc)) from exc
|
|
63
|
+
except ValueError as exc:
|
|
64
|
+
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
|
65
|
+
|
|
66
|
+
@app.get("/health")
|
|
67
|
+
def health():
|
|
68
|
+
return {"ok": True, "busy": service.busy, "session_id": service.current_session_id}
|
|
69
|
+
|
|
70
|
+
@app.get("/status")
|
|
71
|
+
def status():
|
|
72
|
+
return jsonable(service.status())
|
|
73
|
+
|
|
74
|
+
@app.get("/config")
|
|
75
|
+
def config():
|
|
76
|
+
return jsonable(service.config())
|
|
77
|
+
|
|
78
|
+
@app.patch("/config")
|
|
79
|
+
def update_config(request: ConfigUpdateRequest):
|
|
80
|
+
return jsonable(busy_guard(lambda: service.update_config(request.values, save=request.save)))
|
|
81
|
+
|
|
82
|
+
@app.get("/session")
|
|
83
|
+
def session():
|
|
84
|
+
return jsonable(service.session())
|
|
85
|
+
|
|
86
|
+
@app.get("/sessions")
|
|
87
|
+
def sessions():
|
|
88
|
+
return jsonable(service.sessions())
|
|
89
|
+
|
|
90
|
+
@app.post("/sessions/new")
|
|
91
|
+
def start_new_session():
|
|
92
|
+
return jsonable(busy_guard(service.start_new_session))
|
|
93
|
+
|
|
94
|
+
@app.post("/sessions/{session_id}/load")
|
|
95
|
+
def load_session(session_id: str):
|
|
96
|
+
return jsonable(busy_guard(lambda: service.load_session(session_id)))
|
|
97
|
+
|
|
98
|
+
@app.delete("/sessions/{session_id}")
|
|
99
|
+
def delete_session(session_id: str):
|
|
100
|
+
return jsonable(busy_guard(lambda: service.delete_session(session_id)))
|
|
101
|
+
|
|
102
|
+
@app.post("/sessions/navigate/{direction}")
|
|
103
|
+
def navigate_session(direction: str):
|
|
104
|
+
return jsonable(busy_guard(lambda: service.navigate_session(direction)))
|
|
105
|
+
|
|
106
|
+
@app.get("/messages")
|
|
107
|
+
def messages():
|
|
108
|
+
return jsonable(service.messages())
|
|
109
|
+
|
|
110
|
+
@app.get("/memory")
|
|
111
|
+
def memory():
|
|
112
|
+
return jsonable(service.memory())
|
|
113
|
+
|
|
114
|
+
@app.get("/tools")
|
|
115
|
+
def tools():
|
|
116
|
+
return jsonable(service.tools())
|
|
117
|
+
|
|
118
|
+
@app.get("/tui")
|
|
119
|
+
def tui():
|
|
120
|
+
return jsonable(service.tui())
|
|
121
|
+
|
|
122
|
+
@app.post("/tui/open")
|
|
123
|
+
def open_tui(request: Request):
|
|
124
|
+
base_url = str(request.base_url).rstrip("/")
|
|
125
|
+
return jsonable(busy_guard(lambda: service.open_tui(base_url=base_url)))
|
|
126
|
+
|
|
127
|
+
@app.post("/tui/close")
|
|
128
|
+
def close_tui():
|
|
129
|
+
return jsonable(service.close_tui())
|
|
130
|
+
|
|
131
|
+
@app.post("/restart")
|
|
132
|
+
@app.post("/restart/")
|
|
133
|
+
def restart(request: RestartRequest):
|
|
134
|
+
return jsonable(busy_guard(lambda: service.restart(**request.model_dump())))
|
|
135
|
+
|
|
136
|
+
@app.get("/wakeups")
|
|
137
|
+
def wakeups(include_done: bool = True):
|
|
138
|
+
return jsonable(service.wakeups(include_done=include_done))
|
|
139
|
+
|
|
140
|
+
@app.post("/wakeups", status_code=201)
|
|
141
|
+
def schedule_wakeup(request: WakeupRequest):
|
|
142
|
+
return jsonable(busy_guard(lambda: service.schedule_wakeup(**request.model_dump())))
|
|
143
|
+
|
|
144
|
+
@app.post("/wakeups/{job_id}/cancel")
|
|
145
|
+
def cancel_wakeup(job_id: str):
|
|
146
|
+
return jsonable(busy_guard(lambda: service.cancel_wakeup(job_id)))
|
|
147
|
+
|
|
148
|
+
@app.delete("/wakeups/{job_id}")
|
|
149
|
+
def delete_wakeup(job_id: str):
|
|
150
|
+
return jsonable(busy_guard(lambda: service.delete_wakeup(job_id)))
|
|
151
|
+
|
|
152
|
+
@app.post("/turns", status_code=202)
|
|
153
|
+
def turns(request: TurnRequest):
|
|
154
|
+
result = busy_guard(lambda: service.submit_prompt(request.prompt))
|
|
155
|
+
return {"accepted": True, **jsonable(result)}
|
|
156
|
+
|
|
157
|
+
@app.post("/interrupt")
|
|
158
|
+
def interrupt(request: InterruptRequest):
|
|
159
|
+
return jsonable(service.interrupt(request.reason))
|
|
160
|
+
|
|
161
|
+
@app.get("/events")
|
|
162
|
+
def events(limit: int | None = None, after_sequence: int | None = None, client_pid: int | None = None):
|
|
163
|
+
try:
|
|
164
|
+
subscription = service.subscribe(after_sequence=after_sequence, client_pid=client_pid)
|
|
165
|
+
except RuntimeError as exc:
|
|
166
|
+
raise HTTPException(status_code=409, detail=str(exc)) from exc
|
|
167
|
+
|
|
168
|
+
def stream():
|
|
169
|
+
count = 0
|
|
170
|
+
for payload in subscription:
|
|
171
|
+
lines = []
|
|
172
|
+
if payload.get("id"):
|
|
173
|
+
lines.append(f"id: {payload.id}")
|
|
174
|
+
lines.append(f"event: {payload.get('type', 'message')}")
|
|
175
|
+
lines.append(f"data: {json.dumps(payload, ensure_ascii=False, default=str)}")
|
|
176
|
+
yield "\n".join(lines) + "\n\n"
|
|
177
|
+
count += 1
|
|
178
|
+
if limit is not None and count >= limit:
|
|
179
|
+
break
|
|
180
|
+
return StreamingResponse(stream(), media_type="text/event-stream")
|
|
181
|
+
|
|
182
|
+
@app.get("/events/replay")
|
|
183
|
+
def replay_events(before_sequence: int | None = None, session_id: str | None = None):
|
|
184
|
+
return jsonable(service.replay_events(before_sequence=before_sequence, session_id=session_id))
|
|
185
|
+
|
|
186
|
+
return app
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
app = None
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def main():
|
|
193
|
+
import uvicorn
|
|
194
|
+
|
|
195
|
+
global app
|
|
196
|
+
app = create_app(start_agent=True, process_agent=True)
|
|
197
|
+
uvicorn.run(app, host="127.0.0.1", port=8765, timeout_graceful_shutdown=2)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
if __name__ == "__main__":
|
|
201
|
+
main()
|
|
@@ -8,14 +8,14 @@ from collections import deque
|
|
|
8
8
|
|
|
9
9
|
from modict import modict
|
|
10
10
|
|
|
11
|
-
from
|
|
12
|
-
from
|
|
13
|
-
from
|
|
14
|
-
from
|
|
15
|
-
from
|
|
16
|
-
from
|
|
17
|
-
from
|
|
18
|
-
from
|
|
11
|
+
from ..agent import Agent
|
|
12
|
+
from ..agent_runtime import AgentRuntime
|
|
13
|
+
from ..event import Event
|
|
14
|
+
from ..message import UserMessage
|
|
15
|
+
from ..sessions import AgentSession
|
|
16
|
+
from ..service import pop_restart_wakeup, restart_server_service, write_restart_wakeup
|
|
17
|
+
from ..utils import ensure_runtime_dir, runtime_join
|
|
18
|
+
from ..tui import clear_tui, current_tui, open_tui_terminal, terminate_tui
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
|
|
@@ -266,6 +266,16 @@ class AgentServer:
|
|
|
266
266
|
for subscriber, state in list(self.subscribers.items()):
|
|
267
267
|
if not state.thread.is_alive():
|
|
268
268
|
self.subscribers.pop(subscriber, None)
|
|
269
|
+
return len(self.subscribers)
|
|
270
|
+
|
|
271
|
+
def subscriber_count(self):
|
|
272
|
+
if not self.subscriber_lock.acquire(timeout=0.05):
|
|
273
|
+
return len(self.subscribers)
|
|
274
|
+
try:
|
|
275
|
+
self.prune_subscribers()
|
|
276
|
+
return len(self.subscribers)
|
|
277
|
+
finally:
|
|
278
|
+
self.subscriber_lock.release()
|
|
269
279
|
|
|
270
280
|
def close_subscribers(self, reason="server shutdown"):
|
|
271
281
|
self.shutdown_requested.set()
|
|
@@ -277,7 +287,9 @@ class AgentServer:
|
|
|
277
287
|
def subscribe(self, after_sequence=None, client_pid=None):
|
|
278
288
|
after_sequence = int(after_sequence) if after_sequence is not None else None
|
|
279
289
|
client_pid = int(client_pid) if client_pid is not None else None
|
|
280
|
-
|
|
290
|
+
if not self.subscriber_lock.acquire(timeout=1.0):
|
|
291
|
+
raise RuntimeError("event subscription manager is busy")
|
|
292
|
+
try:
|
|
281
293
|
self.shutdown_requested.clear()
|
|
282
294
|
self.prune_subscribers()
|
|
283
295
|
tui = current_tui()
|
|
@@ -297,6 +309,8 @@ class AgentServer:
|
|
|
297
309
|
and int(event.get("sequence") or 0) <= replay_before_sequence
|
|
298
310
|
and self.event_belongs_to_session(event, session_id)
|
|
299
311
|
]
|
|
312
|
+
finally:
|
|
313
|
+
self.subscriber_lock.release()
|
|
300
314
|
def stream():
|
|
301
315
|
try:
|
|
302
316
|
yield modict(
|
|
@@ -321,18 +335,20 @@ class AgentServer:
|
|
|
321
335
|
if payload.get("type") == "server_shutdown":
|
|
322
336
|
break
|
|
323
337
|
finally:
|
|
324
|
-
|
|
338
|
+
if not self.subscriber_lock.acquire(timeout=1.0):
|
|
339
|
+
return
|
|
340
|
+
try:
|
|
325
341
|
self.subscribers.pop(subscriber, None)
|
|
342
|
+
finally:
|
|
343
|
+
self.subscriber_lock.release()
|
|
326
344
|
return stream()
|
|
327
345
|
|
|
328
346
|
def status(self):
|
|
329
|
-
with self.subscriber_lock:
|
|
330
|
-
self.prune_subscribers()
|
|
331
347
|
status = self.runtime.get_status()
|
|
332
348
|
status.update(
|
|
333
349
|
busy=self.busy,
|
|
334
350
|
current_turn_id=self.current_turn_id,
|
|
335
|
-
subscribers=
|
|
351
|
+
subscribers=self.subscriber_count(),
|
|
336
352
|
event_backlog=len(self.events),
|
|
337
353
|
)
|
|
338
354
|
self.persist_current_session()
|
|
@@ -483,198 +499,3 @@ class AgentServer:
|
|
|
483
499
|
raise RuntimeError("agent is busy")
|
|
484
500
|
|
|
485
501
|
|
|
486
|
-
def create_app(agent: Agent | None = None, start_agent=False, process_agent=False, **agent_kwargs):
|
|
487
|
-
from contextlib import asynccontextmanager
|
|
488
|
-
|
|
489
|
-
from fastapi import FastAPI, HTTPException, Request
|
|
490
|
-
from fastapi.responses import StreamingResponse
|
|
491
|
-
from pydantic import BaseModel
|
|
492
|
-
|
|
493
|
-
if process_agent and agent is None:
|
|
494
|
-
if "session" not in agent_kwargs:
|
|
495
|
-
agent_kwargs["session"] = remembered_server_session() or "latest"
|
|
496
|
-
runtime = ProcessAgentRuntime(agent_kwargs=agent_kwargs)
|
|
497
|
-
else:
|
|
498
|
-
runtime = InProcessAgentRuntime(agent or create_server_agent(**agent_kwargs))
|
|
499
|
-
service = AgentServer(runtime=runtime)
|
|
500
|
-
if start_agent:
|
|
501
|
-
service.start()
|
|
502
|
-
|
|
503
|
-
@asynccontextmanager
|
|
504
|
-
async def lifespan(app):
|
|
505
|
-
try:
|
|
506
|
-
yield
|
|
507
|
-
finally:
|
|
508
|
-
service.stop(timeout=1.0)
|
|
509
|
-
|
|
510
|
-
app = FastAPI(title="Codex Agent", version="0.1.0", lifespan=lifespan)
|
|
511
|
-
app.state.agent_service = service
|
|
512
|
-
|
|
513
|
-
class TurnRequest(BaseModel):
|
|
514
|
-
prompt: str
|
|
515
|
-
|
|
516
|
-
class InterruptRequest(BaseModel):
|
|
517
|
-
reason: str = "api"
|
|
518
|
-
|
|
519
|
-
class ConfigUpdateRequest(BaseModel):
|
|
520
|
-
values: dict = {}
|
|
521
|
-
save: bool = True
|
|
522
|
-
|
|
523
|
-
class WakeupRequest(BaseModel):
|
|
524
|
-
prompt: str
|
|
525
|
-
run_at: str | None = None
|
|
526
|
-
delay_seconds: float | None = None
|
|
527
|
-
interval_seconds: float | None = None
|
|
528
|
-
title: str = ""
|
|
529
|
-
|
|
530
|
-
class RestartRequest(BaseModel):
|
|
531
|
-
prompt: str | None = None
|
|
532
|
-
title: str = "post-restart wakeup"
|
|
533
|
-
|
|
534
|
-
def jsonable(value):
|
|
535
|
-
return json.loads(json.dumps(value, ensure_ascii=False, default=str))
|
|
536
|
-
|
|
537
|
-
def busy_guard(call):
|
|
538
|
-
try:
|
|
539
|
-
return call()
|
|
540
|
-
except RuntimeError as exc:
|
|
541
|
-
raise HTTPException(status_code=409, detail=str(exc)) from exc
|
|
542
|
-
except ValueError as exc:
|
|
543
|
-
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
|
544
|
-
|
|
545
|
-
@app.get("/health")
|
|
546
|
-
def health():
|
|
547
|
-
return {"ok": True, "busy": service.busy, "session_id": service.current_session_id}
|
|
548
|
-
|
|
549
|
-
@app.get("/status")
|
|
550
|
-
def status():
|
|
551
|
-
return jsonable(service.status())
|
|
552
|
-
|
|
553
|
-
@app.get("/config")
|
|
554
|
-
def config():
|
|
555
|
-
return jsonable(service.config())
|
|
556
|
-
|
|
557
|
-
@app.patch("/config")
|
|
558
|
-
def update_config(request: ConfigUpdateRequest):
|
|
559
|
-
return jsonable(busy_guard(lambda: service.update_config(request.values, save=request.save)))
|
|
560
|
-
|
|
561
|
-
@app.get("/session")
|
|
562
|
-
def session():
|
|
563
|
-
return jsonable(service.session())
|
|
564
|
-
|
|
565
|
-
@app.get("/sessions")
|
|
566
|
-
def sessions():
|
|
567
|
-
return jsonable(service.sessions())
|
|
568
|
-
|
|
569
|
-
@app.post("/sessions/new")
|
|
570
|
-
def start_new_session():
|
|
571
|
-
return jsonable(busy_guard(service.start_new_session))
|
|
572
|
-
|
|
573
|
-
@app.post("/sessions/{session_id}/load")
|
|
574
|
-
def load_session(session_id: str):
|
|
575
|
-
return jsonable(busy_guard(lambda: service.load_session(session_id)))
|
|
576
|
-
|
|
577
|
-
@app.delete("/sessions/{session_id}")
|
|
578
|
-
def delete_session(session_id: str):
|
|
579
|
-
return jsonable(busy_guard(lambda: service.delete_session(session_id)))
|
|
580
|
-
|
|
581
|
-
@app.post("/sessions/navigate/{direction}")
|
|
582
|
-
def navigate_session(direction: str):
|
|
583
|
-
return jsonable(busy_guard(lambda: service.navigate_session(direction)))
|
|
584
|
-
|
|
585
|
-
@app.get("/messages")
|
|
586
|
-
def messages():
|
|
587
|
-
return jsonable(service.messages())
|
|
588
|
-
|
|
589
|
-
@app.get("/memory")
|
|
590
|
-
def memory():
|
|
591
|
-
return jsonable(service.memory())
|
|
592
|
-
|
|
593
|
-
@app.get("/tools")
|
|
594
|
-
def tools():
|
|
595
|
-
return jsonable(service.tools())
|
|
596
|
-
|
|
597
|
-
@app.get("/tui")
|
|
598
|
-
def tui():
|
|
599
|
-
return jsonable(service.tui())
|
|
600
|
-
|
|
601
|
-
@app.post("/tui/open")
|
|
602
|
-
def open_tui(request: Request):
|
|
603
|
-
base_url = str(request.base_url).rstrip("/")
|
|
604
|
-
return jsonable(busy_guard(lambda: service.open_tui(base_url=base_url)))
|
|
605
|
-
|
|
606
|
-
@app.post("/tui/close")
|
|
607
|
-
def close_tui():
|
|
608
|
-
return jsonable(service.close_tui())
|
|
609
|
-
|
|
610
|
-
@app.post("/restart")
|
|
611
|
-
@app.post("/restart/")
|
|
612
|
-
def restart(request: RestartRequest):
|
|
613
|
-
return jsonable(busy_guard(lambda: service.restart(**request.model_dump())))
|
|
614
|
-
|
|
615
|
-
@app.get("/wakeups")
|
|
616
|
-
def wakeups(include_done: bool = True):
|
|
617
|
-
return jsonable(service.wakeups(include_done=include_done))
|
|
618
|
-
|
|
619
|
-
@app.post("/wakeups", status_code=201)
|
|
620
|
-
def schedule_wakeup(request: WakeupRequest):
|
|
621
|
-
return jsonable(busy_guard(lambda: service.schedule_wakeup(**request.model_dump())))
|
|
622
|
-
|
|
623
|
-
@app.post("/wakeups/{job_id}/cancel")
|
|
624
|
-
def cancel_wakeup(job_id: str):
|
|
625
|
-
return jsonable(busy_guard(lambda: service.cancel_wakeup(job_id)))
|
|
626
|
-
|
|
627
|
-
@app.delete("/wakeups/{job_id}")
|
|
628
|
-
def delete_wakeup(job_id: str):
|
|
629
|
-
return jsonable(busy_guard(lambda: service.delete_wakeup(job_id)))
|
|
630
|
-
|
|
631
|
-
@app.post("/turns", status_code=202)
|
|
632
|
-
def turns(request: TurnRequest):
|
|
633
|
-
result = busy_guard(lambda: service.submit_prompt(request.prompt))
|
|
634
|
-
return {"accepted": True, **jsonable(result)}
|
|
635
|
-
|
|
636
|
-
@app.post("/interrupt")
|
|
637
|
-
def interrupt(request: InterruptRequest):
|
|
638
|
-
return jsonable(service.interrupt(request.reason))
|
|
639
|
-
|
|
640
|
-
@app.get("/events")
|
|
641
|
-
def events(limit: int | None = None, after_sequence: int | None = None, client_pid: int | None = None):
|
|
642
|
-
try:
|
|
643
|
-
subscription = service.subscribe(after_sequence=after_sequence, client_pid=client_pid)
|
|
644
|
-
except RuntimeError as exc:
|
|
645
|
-
raise HTTPException(status_code=409, detail=str(exc)) from exc
|
|
646
|
-
|
|
647
|
-
def stream():
|
|
648
|
-
count = 0
|
|
649
|
-
for payload in subscription:
|
|
650
|
-
lines = []
|
|
651
|
-
if payload.get("id"):
|
|
652
|
-
lines.append(f"id: {payload.id}")
|
|
653
|
-
lines.append(f"event: {payload.get('type', 'message')}")
|
|
654
|
-
lines.append(f"data: {json.dumps(payload, ensure_ascii=False, default=str)}")
|
|
655
|
-
yield "\n".join(lines) + "\n\n"
|
|
656
|
-
count += 1
|
|
657
|
-
if limit is not None and count >= limit:
|
|
658
|
-
break
|
|
659
|
-
return StreamingResponse(stream(), media_type="text/event-stream")
|
|
660
|
-
|
|
661
|
-
@app.get("/events/replay")
|
|
662
|
-
def replay_events(before_sequence: int | None = None, session_id: str | None = None):
|
|
663
|
-
return jsonable(service.replay_events(before_sequence=before_sequence, session_id=session_id))
|
|
664
|
-
|
|
665
|
-
return app
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
app = None
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
def main():
|
|
672
|
-
import uvicorn
|
|
673
|
-
|
|
674
|
-
global app
|
|
675
|
-
app = create_app(start_agent=True, process_agent=True)
|
|
676
|
-
uvicorn.run(app, host="127.0.0.1", port=8765, timeout_graceful_shutdown=2)
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
if __name__ == "__main__":
|
|
680
|
-
main()
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""Textual TUI client package."""
|
|
2
|
+
|
|
3
|
+
from .chat import Chat
|
|
4
|
+
from .lifecycle import (
|
|
5
|
+
clear_tui,
|
|
6
|
+
current_tui,
|
|
7
|
+
install_tui_cleanup_handlers,
|
|
8
|
+
open_tui_terminal,
|
|
9
|
+
register_tui,
|
|
10
|
+
terminal_command_candidates,
|
|
11
|
+
terminal_process_kwargs,
|
|
12
|
+
terminate_tui,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
"Chat",
|
|
17
|
+
"clear_tui",
|
|
18
|
+
"current_tui",
|
|
19
|
+
"install_tui_cleanup_handlers",
|
|
20
|
+
"open_tui_terminal",
|
|
21
|
+
"register_tui",
|
|
22
|
+
"terminal_command_candidates",
|
|
23
|
+
"terminal_process_kwargs",
|
|
24
|
+
"terminate_tui",
|
|
25
|
+
]
|
{codex_agent_framework-0.1.16/codex_agent → codex_agent_framework-0.1.17/codex_agent/tui}/chat.py
RENAMED
|
@@ -3,8 +3,8 @@ import sys
|
|
|
3
3
|
|
|
4
4
|
from rich.text import Text
|
|
5
5
|
|
|
6
|
-
from
|
|
7
|
-
from
|
|
6
|
+
from ..client import AgentClient
|
|
7
|
+
from ..event import (
|
|
8
8
|
AgentTurnStartEvent,
|
|
9
9
|
AgentTurnEndEvent,
|
|
10
10
|
AgentTurnErrorEvent,
|
|
@@ -18,7 +18,7 @@ from .event import (
|
|
|
18
18
|
ServerToolCallEvent,
|
|
19
19
|
ToolCallStartEvent,
|
|
20
20
|
)
|
|
21
|
-
from .
|
|
21
|
+
from .lifecycle import clear_tui, install_tui_cleanup_handlers, register_tui
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
class Chat:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codex-agent-framework
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.17
|
|
4
4
|
Summary: A lightweight event-driven Codex agent runtime.
|
|
5
5
|
Author: Baptiste
|
|
6
6
|
License-Expression: MIT
|
|
@@ -442,6 +442,7 @@ python -m build
|
|
|
442
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`.
|
|
443
443
|
|
|
444
444
|
## Recent changes
|
|
445
|
+
- `0.1.17`: reorganize the TUI and FastAPI server into dedicated `codex_agent.tui` and `codex_agent.server` packages, remove obsolete message payload fields, and clean unused imports.
|
|
445
446
|
- `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
447
|
- `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.
|
|
447
448
|
- `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.
|
|
@@ -11,7 +11,6 @@ codex_agent/agent_runtime.py
|
|
|
11
11
|
codex_agent/ai.py
|
|
12
12
|
codex_agent/builtin_commands.py
|
|
13
13
|
codex_agent/builtin_providers.py
|
|
14
|
-
codex_agent/chat.py
|
|
15
14
|
codex_agent/client.py
|
|
16
15
|
codex_agent/command.py
|
|
17
16
|
codex_agent/config.py
|
|
@@ -28,14 +27,12 @@ codex_agent/provider.py
|
|
|
28
27
|
codex_agent/registry.py
|
|
29
28
|
codex_agent/runtime.py
|
|
30
29
|
codex_agent/scheduler.py
|
|
31
|
-
codex_agent/server.py
|
|
32
30
|
codex_agent/service.py
|
|
33
31
|
codex_agent/sessions.py
|
|
34
32
|
codex_agent/status.py
|
|
35
33
|
codex_agent/stream_utils.py
|
|
36
34
|
codex_agent/tool.py
|
|
37
35
|
codex_agent/tray.py
|
|
38
|
-
codex_agent/tui.py
|
|
39
36
|
codex_agent/utils.py
|
|
40
37
|
codex_agent/voice.py
|
|
41
38
|
codex_agent/worker.py
|
|
@@ -67,6 +64,12 @@ codex_agent/get_text/simpler_get_text.py
|
|
|
67
64
|
codex_agent/prompts/image_generation_system_prompt.txt
|
|
68
65
|
codex_agent/prompts/system_prompt.txt
|
|
69
66
|
codex_agent/scripts/install-system-dependencies.sh
|
|
67
|
+
codex_agent/server/__init__.py
|
|
68
|
+
codex_agent/server/app.py
|
|
69
|
+
codex_agent/server/core.py
|
|
70
|
+
codex_agent/tui/__init__.py
|
|
71
|
+
codex_agent/tui/chat.py
|
|
72
|
+
codex_agent/tui/lifecycle.py
|
|
70
73
|
codex_agent_framework.egg-info/PKG-INFO
|
|
71
74
|
codex_agent_framework.egg-info/SOURCES.txt
|
|
72
75
|
codex_agent_framework.egg-info/dependency_links.txt
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from modict import modict
|
|
2
2
|
|
|
3
3
|
from codex_agent import Agent
|
|
4
|
-
from codex_agent.chat import Chat
|
|
4
|
+
from codex_agent.tui.chat import Chat
|
|
5
5
|
from codex_agent.message import CompactionMessage, DeveloperMessage, UserMessage
|
|
6
6
|
|
|
7
7
|
|
|
@@ -308,7 +308,7 @@ def test_chat_replays_startup_events_after_subscription(monkeypatch):
|
|
|
308
308
|
|
|
309
309
|
chat.app = App()
|
|
310
310
|
chat.attach()
|
|
311
|
-
monkeypatch.setattr("codex_agent.chat.register_tui", lambda base_url: None)
|
|
311
|
+
monkeypatch.setattr("codex_agent.tui.chat.register_tui", lambda base_url: None)
|
|
312
312
|
monkeypatch.setattr(
|
|
313
313
|
chat.client,
|
|
314
314
|
"replay_events",
|
|
@@ -49,7 +49,7 @@ def test_agent_server_keeps_slow_subscribers_attached():
|
|
|
49
49
|
def test_agent_server_refuses_second_event_subscriber(monkeypatch):
|
|
50
50
|
agent = Agent(session="new", voice_enabled=False)
|
|
51
51
|
service = AgentServer(InProcessAgentRuntime(agent))
|
|
52
|
-
monkeypatch.setattr("codex_agent.server.current_tui", lambda: modict(pid=1234))
|
|
52
|
+
monkeypatch.setattr("codex_agent.server.core.current_tui", lambda: modict(pid=1234))
|
|
53
53
|
stream = service.subscribe()
|
|
54
54
|
|
|
55
55
|
next(stream)
|
|
@@ -318,9 +318,9 @@ def test_agent_server_open_tui_returns_registered_client(monkeypatch):
|
|
|
318
318
|
service = AgentServer(InProcessAgentRuntime(agent))
|
|
319
319
|
tui = modict(pid=4321, base_url="http://agent.local")
|
|
320
320
|
|
|
321
|
-
monkeypatch.setattr("codex_agent.server.current_tui", lambda: tui)
|
|
321
|
+
monkeypatch.setattr("codex_agent.server.core.current_tui", lambda: tui)
|
|
322
322
|
monkeypatch.setattr(
|
|
323
|
-
"codex_agent.server.open_tui_terminal",
|
|
323
|
+
"codex_agent.server.core.open_tui_terminal",
|
|
324
324
|
lambda **kwargs: (_ for _ in ()).throw(AssertionError("terminal should not be opened")),
|
|
325
325
|
)
|
|
326
326
|
|
|
@@ -337,8 +337,8 @@ def test_agent_server_open_tui_launches_terminal_when_no_client(monkeypatch):
|
|
|
337
337
|
class Process:
|
|
338
338
|
pid = 9876
|
|
339
339
|
|
|
340
|
-
monkeypatch.setattr("codex_agent.server.current_tui", lambda: None)
|
|
341
|
-
monkeypatch.setattr("codex_agent.server.open_tui_terminal", lambda base_url: Process())
|
|
340
|
+
monkeypatch.setattr("codex_agent.server.core.current_tui", lambda: None)
|
|
341
|
+
monkeypatch.setattr("codex_agent.server.core.open_tui_terminal", lambda base_url: Process())
|
|
342
342
|
|
|
343
343
|
result = service.open_tui(base_url="http://agent.local")
|
|
344
344
|
|
|
@@ -352,9 +352,9 @@ def test_agent_server_close_tui_terminates_registered_pid(monkeypatch):
|
|
|
352
352
|
service = AgentServer(InProcessAgentRuntime(agent))
|
|
353
353
|
calls = []
|
|
354
354
|
|
|
355
|
-
monkeypatch.setattr("codex_agent.server.current_tui", lambda: modict(pid=2468))
|
|
356
|
-
monkeypatch.setattr("codex_agent.server.terminate_tui", lambda pid: calls.append(("terminate", pid)))
|
|
357
|
-
monkeypatch.setattr("codex_agent.server.clear_tui", lambda pid=None: calls.append(("clear", pid)))
|
|
355
|
+
monkeypatch.setattr("codex_agent.server.core.current_tui", lambda: modict(pid=2468))
|
|
356
|
+
monkeypatch.setattr("codex_agent.server.core.terminate_tui", lambda pid: calls.append(("terminate", pid)))
|
|
357
|
+
monkeypatch.setattr("codex_agent.server.core.clear_tui", lambda pid=None: calls.append(("clear", pid)))
|
|
358
358
|
|
|
359
359
|
result = service.close_tui()
|
|
360
360
|
|
|
@@ -417,7 +417,7 @@ def test_agent_server_refuses_second_subscriber_when_tui_is_registered(monkeypat
|
|
|
417
417
|
agent = Agent(session="new", voice_enabled=False)
|
|
418
418
|
service = AgentServer(InProcessAgentRuntime(agent))
|
|
419
419
|
service.subscribers[queue.Queue()] = modict(thread=threading.current_thread())
|
|
420
|
-
monkeypatch.setattr("codex_agent.server.current_tui", lambda: modict(pid=1234))
|
|
420
|
+
monkeypatch.setattr("codex_agent.server.core.current_tui", lambda: modict(pid=1234))
|
|
421
421
|
|
|
422
422
|
try:
|
|
423
423
|
try:
|
|
@@ -434,7 +434,7 @@ def test_agent_server_replaces_stale_subscriber_for_same_tui_pid(monkeypatch):
|
|
|
434
434
|
agent = Agent(session="new", voice_enabled=False)
|
|
435
435
|
service = AgentServer(InProcessAgentRuntime(agent))
|
|
436
436
|
service.subscribers[queue.Queue()] = modict(thread=threading.current_thread(), client_pid=1234)
|
|
437
|
-
monkeypatch.setattr("codex_agent.server.current_tui", lambda: modict(pid=1234))
|
|
437
|
+
monkeypatch.setattr("codex_agent.server.core.current_tui", lambda: modict(pid=1234))
|
|
438
438
|
|
|
439
439
|
stream = service.subscribe(client_pid=1234)
|
|
440
440
|
try:
|
|
@@ -449,7 +449,7 @@ def test_agent_server_replaces_stale_subscriber_when_no_tui_is_registered(monkey
|
|
|
449
449
|
agent = Agent(session="new", voice_enabled=False)
|
|
450
450
|
service = AgentServer(InProcessAgentRuntime(agent))
|
|
451
451
|
service.subscribers[queue.Queue()] = modict(thread=threading.current_thread())
|
|
452
|
-
monkeypatch.setattr("codex_agent.server.current_tui", lambda: None)
|
|
452
|
+
monkeypatch.setattr("codex_agent.server.core.current_tui", lambda: None)
|
|
453
453
|
|
|
454
454
|
stream = service.subscribe()
|
|
455
455
|
try:
|
|
@@ -465,7 +465,7 @@ def test_create_app_refuses_second_sse_client(monkeypatch):
|
|
|
465
465
|
client = TestClient(app)
|
|
466
466
|
service = app.state.agent_service
|
|
467
467
|
service.subscribers[queue.Queue()] = modict(thread=threading.current_thread())
|
|
468
|
-
monkeypatch.setattr("codex_agent.server.current_tui", lambda: modict(pid=1234))
|
|
468
|
+
monkeypatch.setattr("codex_agent.server.core.current_tui", lambda: modict(pid=1234))
|
|
469
469
|
|
|
470
470
|
try:
|
|
471
471
|
second = client.get("/events?limit=1")
|
|
@@ -531,7 +531,7 @@ def test_agent_server_consumes_restart_wakeup(monkeypatch):
|
|
|
531
531
|
agent = Agent(session="new", voice_enabled=False)
|
|
532
532
|
service = AgentServer(InProcessAgentRuntime(agent))
|
|
533
533
|
monkeypatch.setattr(
|
|
534
|
-
"codex_agent.server.pop_restart_wakeup",
|
|
534
|
+
"codex_agent.server.core.pop_restart_wakeup",
|
|
535
535
|
lambda: {"prompt": "continue work", "title": "resume"},
|
|
536
536
|
)
|
|
537
537
|
calls = []
|
|
@@ -550,15 +550,15 @@ def test_agent_server_restart_persists_wakeup_and_restarts_service(monkeypatch):
|
|
|
550
550
|
agent = Agent(session="new", voice_enabled=False)
|
|
551
551
|
service = AgentServer(InProcessAgentRuntime(agent))
|
|
552
552
|
calls = []
|
|
553
|
-
monkeypatch.setattr("codex_agent.server.write_restart_wakeup", lambda **kwargs: calls.append(("wakeup", kwargs)))
|
|
554
|
-
monkeypatch.setattr("codex_agent.server.time.sleep", lambda delay: calls.append(("sleep", delay)))
|
|
555
|
-
monkeypatch.setattr("codex_agent.server.restart_server_service", lambda: calls.append(("restart",)))
|
|
553
|
+
monkeypatch.setattr("codex_agent.server.core.write_restart_wakeup", lambda **kwargs: calls.append(("wakeup", kwargs)))
|
|
554
|
+
monkeypatch.setattr("codex_agent.server.core.time.sleep", lambda delay: calls.append(("sleep", delay)))
|
|
555
|
+
monkeypatch.setattr("codex_agent.server.core.restart_server_service", lambda: calls.append(("restart",)))
|
|
556
556
|
class ImmediateThread:
|
|
557
557
|
def __init__(self, target, **kwargs):
|
|
558
558
|
self.target = target
|
|
559
559
|
def start(self):
|
|
560
560
|
self.target()
|
|
561
|
-
monkeypatch.setattr("codex_agent.server.threading.Thread", ImmediateThread)
|
|
561
|
+
monkeypatch.setattr("codex_agent.server.core.threading.Thread", ImmediateThread)
|
|
562
562
|
result = service.restart(prompt="resume please", title="resume", delay_seconds=0)
|
|
563
563
|
assert result.restarting is True
|
|
564
564
|
assert result.wakeup_persisted is True
|
|
@@ -584,15 +584,15 @@ def test_agent_server_restart_waits_for_turn_end_when_busy(monkeypatch):
|
|
|
584
584
|
service = AgentServer(InProcessAgentRuntime(agent))
|
|
585
585
|
agent.mainloop_manager.futures["running"] = modict(command="turn", status="running")
|
|
586
586
|
calls = []
|
|
587
|
-
monkeypatch.setattr("codex_agent.server.write_restart_wakeup", lambda **kwargs: calls.append(("wakeup", kwargs)))
|
|
588
|
-
monkeypatch.setattr("codex_agent.server.time.sleep", lambda delay: calls.append(("sleep", delay)))
|
|
589
|
-
monkeypatch.setattr("codex_agent.server.restart_server_service", lambda: calls.append(("restart",)))
|
|
587
|
+
monkeypatch.setattr("codex_agent.server.core.write_restart_wakeup", lambda **kwargs: calls.append(("wakeup", kwargs)))
|
|
588
|
+
monkeypatch.setattr("codex_agent.server.core.time.sleep", lambda delay: calls.append(("sleep", delay)))
|
|
589
|
+
monkeypatch.setattr("codex_agent.server.core.restart_server_service", lambda: calls.append(("restart",)))
|
|
590
590
|
class ImmediateThread:
|
|
591
591
|
def __init__(self, target, **kwargs):
|
|
592
592
|
self.target = target
|
|
593
593
|
def start(self):
|
|
594
594
|
self.target()
|
|
595
|
-
monkeypatch.setattr("codex_agent.server.threading.Thread", ImmediateThread)
|
|
595
|
+
monkeypatch.setattr("codex_agent.server.core.threading.Thread", ImmediateThread)
|
|
596
596
|
|
|
597
597
|
result = service.restart(prompt="resume please", title="resume", delay_seconds=0)
|
|
598
598
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/builtin_commands.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/builtin_providers.py
RENAMED
|
File without changes
|
{codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/builtin_tools/__init__.py
RENAMED
|
File without changes
|
{codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/builtin_tools/files.py
RENAMED
|
File without changes
|
|
File without changes
|
{codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/builtin_tools/shell.py
RENAMED
|
File without changes
|
{codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/builtin_tools/system.py
RENAMED
|
File without changes
|
{codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/builtin_tools/vision.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/get_text/__init__.py
RENAMED
|
File without changes
|
{codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/get_text/default_gitignore
RENAMED
|
File without changes
|
{codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/get_text/get_text.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/codex_agent/prompts/system_prompt.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{codex_agent_framework-0.1.16 → codex_agent_framework-0.1.17}/tests/test_get_text_browser.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|