Skip to main content

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

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 is disabled by default. Enable it explicitly for a voice session:

from codex_agent import Agent

agent = Agent(
    session="latest",
    openai_api_key="sk-...",
    voice_enabled=True,
    voice="nova",
)
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 Plugin subclass;
  • a register(agent) function;
  • decorated @tool, @provider, or @command callables 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, 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", "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",
    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 voice_enabled=true
codex-agent config set builtin_plugins='["memory", "planner", "scheduler"]'
codex-agent config set --no-save 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/*

Recent releases

  • 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 /compact and /help show running, done, failed, result, and error feedback instead of going silent.
  • 0.1.23: add SDK event streaming with Agent.stream(), root Agent.add_plugin(), a faster README flow, the ~/.codex-agent runtime default, text-first voice defaults, safer prompt templating limited to system messages, smarter codex-agent local-server attachment, and clearer TUI tool-call progress lines.
  • 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 structured AgentTurn returns with messages/events, resumable non-terminal turns for auto_proceed=False, profile-based subagents, hierarchical message/event types, and internal wakeups as developer messages.
  • 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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

codex_agent_framework-0.1.25.tar.gz (268.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

codex_agent_framework-0.1.25-py3-none-any.whl (211.4 kB view details)

Uploaded Python 3

File details

Details for the file codex_agent_framework-0.1.25.tar.gz.

File metadata

  • Download URL: codex_agent_framework-0.1.25.tar.gz
  • Upload date:
  • Size: 268.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.13

File hashes

Hashes for codex_agent_framework-0.1.25.tar.gz
Algorithm Hash digest
SHA256 ef6a36c3e75d6eac76c392a1d34255e02c2a1849be42c747db8a443a1af05b20
MD5 9bc970590e218e97044a85a11c5514ec
BLAKE2b-256 48cde08f283b3936a02f64fc33bb3960dcbc812a079c46cd1a8a9f4610abe8d8

See more details on using hashes here.

File details

Details for the file codex_agent_framework-0.1.25-py3-none-any.whl.

File metadata

File hashes

Hashes for codex_agent_framework-0.1.25-py3-none-any.whl
Algorithm Hash digest
SHA256 6cbd7ecfcde0bfe2f99bbed925fd169cd5b7b0eeb3f2a1e34557c041e49e32ff
MD5 dd07548ae5f6988650c4b07361ecad87
BLAKE2b-256 b44b53e247232292e938ad1ee200d2efbfb77b8a52027f42ac52e689c4ce831c

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page