A lightweight event-driven Codex agent runtime.
Project description
codex-agent
codex-agent-framework is a local-first Python framework for building and running tool-using AI agents.
Use it as a ready-to-run terminal assistant, or import Agent when you want to build your own workflow in Python.
Early alpha: APIs are still evolving and breaking changes are expected while the architecture settles.
Quick start
Install the package:
python -m pip install codex-agent-framework
For the recommended local setup, run:
codex-agent bootstrap -- -y
bootstrap installs the desktop/browser/tray dependencies, installs the user services, and starts them. It is currently aimed mostly at Debian-like Linux systems. Use an X11 session for the best desktop automation support; Wayland is fine if you do not use the desktop plugin.
Open the assistant:
codex-agent
If a local agent server is already running, the TUI connects to it. Otherwise codex-agent starts a temporary server for that TUI session and shuts it down when the TUI exits.
Inside the TUI, start with:
/help
/sessions
/new_session
/load_session latest
/compact
/config
Use /compact when a long session needs to reduce its active context. It keeps a backend summary and prunes older raw history from the active session file.
Prefer the SDK? Start with this:
from codex_agent import Agent
agent = Agent(session="new")
turn = agent("Summarize this repository in three practical bullet points.")
print(turn.result)
Contents
- Python API
- Core concepts
- Extending the agent
- Built-in capabilities
- Running modes
- Configuration and runtime files
- Development
- Recent releases
- Safety notes
Python API
Minimal agent
from codex_agent import Agent
agent = Agent(session="new", username="Baptiste")
turn = agent("Summarize this repository in three practical bullet points.")
print("completed:", turn.completed)
print("reason:", turn.reason)
print("result:", turn.result)
agent(...) blocks until the turn completes and returns an AgentTurn. Use turn.result for the final result; turn.messages and turn.events contain what happened during the turn.
Stream events from a turn
Use agent.stream(...) when you want live events and the final turn:
from codex_agent import Agent, ResponseContentDeltaEvent
agent = Agent(session="new")
stream = agent.stream("Explain what this project does.")
for event in stream:
if isinstance(event, ResponseContentDeltaEvent):
print(event.delta, end="", flush=True)
turn = stream.turn
print("\ncompleted:", turn.completed)
stream.turn waits for the final AgentTurn; it does not expose a partial turn object.
You can get a similar effect with the event bus and a normal blocking call:
from codex_agent import Agent, ResponseContentDeltaEvent
agent = Agent(session="new")
@agent.on(ResponseContentDeltaEvent)
def print_delta(event):
print(event.delta, end="", flush=True)
turn = agent("Explain what this project does.")
print("\ncompleted:", turn.completed)
Open an interactive session from Python
from codex_agent import Agent
agent = Agent(session="latest")
agent.interact()
Voice features are disabled by default. The realtime voice plugin uses Codex/ChatGPT auth through codex-backend-sdk; the legacy synchronized TTS stream still uses the OpenAI SDK speech endpoint when explicitly enabled.
from codex_agent import Agent
agent = Agent(
session="latest",
builtin_plugins=["realtime", "memory", "planner", "scheduler"],
)
agent.load_builtin_plugin("realtime")
agent.interact()
Add a tool
Tools are actions the model may choose to call.
For a tool without parameters, a plain docstring becomes the tool description:
import subprocess
from codex_agent import Agent, tool
@tool
def list_changed_files() -> list[str]:
"""Return modified or untracked files in the current git repository."""
output = subprocess.check_output(["git", "status", "--short"], text=True)
return [line[3:] for line in output.splitlines() if line.strip()]
agent = Agent(session="latest")
agent.add_tool(list_changed_files)
For a parameterized tool, use a YAML docstring to describe the schema exposed to the model:
from pathlib import Path
from codex_agent import Agent, tool
@tool
def read_project_note(path: str):
"""
description: Read a UTF-8 project note file.
parameters:
path:
type: string
description: Path to the note file, relative to the current project.
required:
- path
"""
return Path(path).read_text(encoding="utf-8")
agent = Agent(session="latest")
agent.add_tool(read_project_note)
agent("Look at my project notes and tell me what changed recently.")
Add live context
Providers inject fresh context before a model call without saving it as permanent conversation history.
from datetime import datetime
from zoneinfo import ZoneInfo
from codex_agent import Agent, provider
@provider
def current_time():
now = datetime.now(ZoneInfo("Europe/Paris"))
return f"Current local time: {now:%Y-%m-%d %H:%M:%S %Z}"
agent = Agent(session="latest")
agent.add_provider(current_time)
agent("Given the current time, should I start a long-running task now?")
Add a slash command
Commands are explicit user actions triggered with /name.
from codex_agent import Agent, command, get_agent
@command
def repo():
"""Show the active session and current repository hint."""
agent = get_agent()
return f"session={agent.current_session_id}; repo=codex-agent"
agent = Agent(session="latest")
agent.add_command(repo)
print(agent("/repo").result)
Core concepts
The framework is built around a few extension points:
| Concept | What it is for |
|---|---|
| Session | Saved conversation and agent state. |
| Turn | One user request plus the model/tool loop needed to answer it. |
| Tool | An action the model may choose to call. |
| Provider | Fresh context injected before a response, not saved as chat history. |
| Command | A user-triggered operation such as /compact or /config. |
| Plugin | A bundle of state, tools, providers, commands, events, hooks, and prompt text. |
Sessions and turns
Agent(session="new") # fresh session
Agent(session="latest") # newest saved session
Agent(session="<session_id>") # specific saved session
Agent(session="/path/to/session.json") # session file
By default, a turn runs until the agent has completed its tool/model loop. auto_proceed=False is an advanced mode for external orchestration: your application can pause after each model step, inspect state, approve or cancel work, and resume explicitly.
agent = Agent(session="new", auto_proceed=False)
turn = agent("Please explore the current repo. Use tools if needed.")
while not turn.completed and turn.reason == "requires_next_step":
# External monitoring, approval, logging, cancellation, etc. can happen here.
turn = agent.resume_turn(turn)
Extending the agent
Use runtime extensions when you want to customize an installed agent without forking the repository.
At startup, the agent scans:
~/.codex-agent/plugins/*.py
~/.codex-agent/commands/*.py
Files starting with _ are ignored.
A plugin module may expose:
- a
Pluginsubclass; - a
register(agent)function; - decorated
@tool,@provider, or@commandcallables for small extensions.
Direct runtime tools/ and providers/ folders are intentionally not loaded. Put simple runtime tools/providers in a plugin module, or use agent.add_tool() and agent.add_provider() in SDK code.
Plugin example
from codex_agent import Agent, Plugin, provider, tool
class ProjectNotesPlugin(Plugin):
name = "project_notes"
system_prompt = "# Project Notes\nUse project notes when relevant."
def __init__(self, agent):
super().__init__(agent)
self.notes = []
@tool
def remember_note(self, note: str):
"""
description: Store a project note that should be available in later turns.
parameters:
note:
type: string
description: The note to remember. Keep it concise and project-specific.
required:
- note
"""
self.notes.append(note)
return f"Stored {len(self.notes)} note(s)."
@provider
def project_notes(self):
if not self.notes:
return None
return "Project notes:\n" + "\n".join(f"- {note}" for note in self.notes)
agent = Agent(session="latest")
agent.add_plugin(ProjectNotesPlugin(agent))
Runtime plugin example
~/.codex-agent/plugins/git_helpers.py:
from datetime import datetime
from zoneinfo import ZoneInfo
import subprocess
from codex_agent import provider, tool
@tool
def current_branch() -> str:
"""Return the current git branch."""
return subprocess.check_output(
["git", "branch", "--show-current"],
text=True,
).strip()
@provider
def current_time():
now = datetime.now(ZoneInfo("Europe/Paris"))
return f"Current local time: {now:%Y-%m-%d %H:%M:%S %Z}"
~/.codex-agent/commands/repo.py:
from codex_agent import command, get_agent
@command(name="repo")
def repo(args=""):
agent = get_agent()
return f"session={agent.current_session_id}; args={args}"
Events and hooks
Agent exposes an event bus for UIs, automation, and plugins:
from codex_agent import Agent, MessageAddedEvent, ToolCallStartEvent
agent = Agent(session="new")
@agent.on(MessageAddedEvent)
def log_message(event):
print("message", event.message.type)
@agent.on(ToolCallStartEvent)
def log_tool(event):
print("tool", event.name)
Plugins and tools may also emit custom events and expose custom hook points:
from codex_agent import Agent, Event, Plugin, tool
class NoteStoredEvent(Event):
note = ""
class NotesPlugin(Plugin):
name = "notes"
@tool
def remember_note(self, note: str):
"""
description: Store a note and notify listeners.
parameters:
note:
type: string
description: Note to store.
required:
- note
"""
context = self.agent.run_hook("notes.before_store", note=note)
if context.stopped:
return "Note rejected."
note = context.payload.note
self.agent.emit(NoteStoredEvent, note=note)
return "Note stored."
agent = Agent(session="new")
agent.add_plugin(NotesPlugin(agent))
@agent.on(NoteStoredEvent)
def log_note(event):
print("stored note:", event.note)
@agent.add_hook("notes.before_store")
def trim_note(context):
context.payload.note = context.payload.note.strip()
Plugin methods can also subscribe declaratively with @event_handler(...) and @hook_handler(...).
Built-in capabilities
The default agent includes practical local tools grouped as plugins:
| Area | Examples | Purpose |
|---|---|---|
| Files | read, view, write, edit |
Strict text reads, broad extraction, exact-string edits. |
| Shell | bash, python |
Shell commands and persistent Python execution. |
| Memory | memory_add, memory_search, /memory_config |
Durable semantic memory and retrieval context. |
| Planner | planner_create, planner_check |
Persistent named todos surfaced as context. |
| Scheduler | scheduler_schedule, scheduler_restart_and_wakeup |
Future turns and post-restart continuation. |
| Browser | browser_open, browser_click, browser_fill |
Persistent Playwright/Chromium automation. |
| Desktop | desktop_start_session, desktop_run_commands |
Screenshot-backed Linux desktop automation. |
| Subagents | subagents_run |
Ephemeral child agents from predefined profiles. |
Current built-in plugin modules include files, content, bash, python, interface, environment, context, memory, planner, scheduler, browser, desktop, and subagents.
The subagents plugin ships an explorer profile: a read-only codebase inspection agent.
Select built-ins explicitly when you want a smaller agent:
agent = Agent(
session="latest",
builtin_plugins=["files", "content", "bash", "python", "environment", "context", "memory", "planner"],
)
None loads all built-ins, [] loads none, and an explicit list selects plugin module names.
Running modes
You can ignore this section for normal TUI usage. It is for services, automation, and integrations.
| Mode | Entry point | Best for |
|---|---|---|
| Embedded Python | Agent(...) |
Scripts, notebooks, tests, custom applications. |
| Interactive TUI | codex-agent |
Daily local assistant usage. |
| Persistent server | codex-agent start server |
Long-lived local service used by UIs or scripts. |
| Headless CLI | codex-agent run ... |
Automation and shell pipelines. |
| Process runtime | codex-agent run --runtime process ... |
One-shot isolated runs without an existing server. |
| Tray/service setup | codex-agent bootstrap |
Desktop availability independent of a terminal. |
Run a persistent background server:
codex-agent start server
codex-agent open tui
Detached logs are written under ~/.codex-agent/logs/server.log.
Useful headless commands:
codex-agent status --json
codex-agent tools
codex-agent config get model
codex-agent run "Run a quick repository health check."
git diff -- README.md | codex-agent run --stdin "Review this documentation diff."
codex-agent run --runtime process "Summarize the current project layout."
codex-agent interrupt "user changed priority"
The local server exposes FastAPI REST/SSE endpoints for health, status, sessions, config, messages, tools, wakeups, live events, replay, turns, interrupts, and restart. The TUI uses SSE, tracks event cursors, replays the latest turn when opened mid-session, and reconnects after server restarts or transient stream loss.
Configuration and runtime files
Configure Agent directly:
from codex_agent import Agent
agent = Agent(
session="latest",
model="gpt-5.5",
reasoning={"effort": "medium", "summary": "auto"},
text={"verbosity": "medium"},
input_token_limit=128000,
auto_compact=True,
web_search_enabled=False,
image_generation_enabled=False,
builtin_plugins=None, # None = all, [] = none, or explicit plugin names
custom_plugins=None, # None = all runtime plugins, [] = none
)
Or update saved config from the CLI:
codex-agent config get
codex-agent config set input_token_limit 128000
codex-agent config set builtin_plugins='["environment", "context", "memory", "planner", "scheduler", "realtime"]'
codex-agent config set --no-save text='{"verbosity":"low"}'
Saved local state lives in ~/.codex-agent by default:
sessions/ persisted conversation histories as JSON
workfolder/ generated or uploaded working files
plugins/ user runtime plugins
commands/ user runtime slash commands
browser/ persistent browser profiles and screenshots
logs/ detached server/tray logs
memory.json durable semantic memory entries
planner.json persistent named todos
wakeups.json scheduled autonomous wakeups
agent.config.json persisted agent configuration
Override it with:
AGENT_RUNTIME_DIR=/tmp/my-codex-agent-runtime codex-agent
Development
Project layout:
codex_agent/ Python package
codex_agent/agent.py Central Agent object and high-level public methods
codex_agent/mainloop.py Turn loop, assistant calls, tool execution, pending results
codex_agent/stream.py SDK event stream wrapper around an AgentFuture
codex_agent/context.py Context assembly and token budgeting
codex_agent/sessions.py Persistent sessions and compaction
codex_agent/tool.py Tool model, decorators, and manager
codex_agent/provider.py Provider decorator and manager
codex_agent/plugin.py Stateful plugin base class and manager
codex_agent/command.py Slash command registry and dispatch
codex_agent/hooks.py Generic runtime hook manager
codex_agent/event.py Event classes and event bus
codex_agent/server/ FastAPI REST/SSE bridge package
codex_agent/tui/ Textual TUI client
codex_agent/cli/ Root CLI and headless runners
codex_agent/builtin_plugins/ Built-in capabilities grouped as plugins
tests/ Test suite
Run checks:
python -m pyflakes codex_agent tests
python -m pytest
The current tree validates at:
546 passed
Build distributions:
python -m pip install build twine
rm -rf build dist *.egg-info
python -m build
python -m twine check dist/*
Auth model for backend-backed features
Core LLM calls, memory embeddings, non-streaming STT transcription, and realtime voice use the Codex backend SDK with Codex/ChatGPT auth. Legacy synchronized TTS playback and OpenAI input-token counting still use the OpenAI SDK and may require openai_api_key or OPENAI_API_KEY when those paths are enabled.
Recent releases
0.1.27: upgrade the agenticreadtool with folder trees, recursive folder pattern search, and precisestart_at_line/end_at_lineranges; add visible wrapper/token split stats, compactable turn counts, and refreshed TUI status-bar context telemetry.0.1.26: align SDK-facing config with Responses API shapes, add thecontextmanagement plugin with selective non-destructive wrapper pruning, keep wrappers out of memory retrieval and compaction payloads, move embeddings/STT/realtime onto Codex backend auth, streamline voice/TTS plugin config, and improve TUI streaming/windowing.0.1.25: move built-in capabilities further into plugin-scoped modules, add bundled plugin requirement metadata, and replace the TUI conversation pane with a selectable rich chat area that preserves mouse scrolling, copy selection, lazy transcript loading, and bottom-aware auto-scroll.0.1.24: add slash-command lifecycle events and TUI progress/result rendering so commands such as/compactand/helpshow running, done, failed, result, and error feedback instead of going silent.0.1.23: add SDK event streaming withAgent.stream(), rootAgent.add_plugin(), a faster README flow, the~/.codex-agentruntime default, text-first voice defaults, safer prompt templating limited to system messages, smartercodex-agentlocal-server attachment, and clearer TUI tool-call progress lines.0.1.22: harden FastAPI/SSE and TUI streaming after a stabilization audit; add finite SSE read timeouts, stale-subscriber shutdown, persisted turn-event locking, synchronous replay delta flushes, process-runtime startup guards, isolated event handlers, and shared version metadata.0.1.21: add structuredAgentTurnreturns with messages/events, resumable non-terminal turns forauto_proceed=False, profile-based subagents, hierarchical message/event types, and internal wakeups as developer messages.0.1.20: make runtime plugins the user-facing extension unit for tools/providers/commands, remove legacy direct runtime tool/provider loading, split tool/provider managers, and keep system-prompt templating on an explicit minimal namespace.
See CHANGELOG.md for the full history.
Safety notes
This project lets an AI assistant act on the local machine. That is useful, but risky.
Recommended practices:
- Use a dedicated runtime directory for experiments.
- Review tool calls before enabling autonomous workflows.
- Avoid running the agent with elevated privileges.
- Keep secrets out of prompts, logs, committed runtime files, and shared session exports.
- Treat browser and desktop automation as real user actions.
- Keep important runtime plugins under version control.
License
MIT. See LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file codex_agent_framework-0.1.28.tar.gz.
File metadata
- Download URL: codex_agent_framework-0.1.28.tar.gz
- Upload date:
- Size: 284.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a593a400985b6d4af40f527326f1df6ecb8b259ac54f2ba49fdb0e4514012b3b
|
|
| MD5 |
9e61f1e20bcd62855f9cf1da9278d4aa
|
|
| BLAKE2b-256 |
6485270bf125bdb30d6ad20ecea9d04a927d7da2776ed9576558241d657a4e65
|
File details
Details for the file codex_agent_framework-0.1.28-py3-none-any.whl.
File metadata
- Download URL: codex_agent_framework-0.1.28-py3-none-any.whl
- Upload date:
- Size: 221.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8960c874773f0c01c8acb7279156599bd3272c4f1ecbde649035273896921bb2
|
|
| MD5 |
8167ae25900328f0c6f2d9cfd017437f
|
|
| BLAKE2b-256 |
f39c38f01e58a8d38b2ef8a91ab50dda4a659d896fe1d62b08a3768cf7fac026
|