AgentLib and Code Agent

A small Python framework for building agents, and an interactive coding agent in the spirit of Claude Code or Codex, built around a Python REPL. Both were built for real work - pricing, research, browser automation, and the day-to-day development of the business itself - and both are public.

public agentlib · code-agent role sole author

The situation#

The business already ran on software I had built - pricing, purchasing, accounting, fulfillment. When language models became genuinely useful, the natural question was not “what can AI do” but “which of my existing problems does this actually help with.” The first real answers were unglamorous: classifying ambiguous customer email, researching competitor listings, an agent that repairs its own browser automation when a marketplace changes its markup, reasoning about a pricing decision too tangled for a formula.

Each of those needed an agent that could touch real systems. And each one immediately ran into the same practical walls - the model needed tools with real types, a runtime that kept state between steps, a way to resume a session that died mid-task, and a way to keep a long investigation’s context from either vanishing or growing without bound.

The constraint#

The constraint was trust, same as everywhere else in the business. An agent that touches production has to be bounded: it sees curated data, it acts through typed tools, and the things that must never go wrong - money, identity, external side effects - stay in deterministic code the model cannot argue with. That ruled out the easy path of pointing a general-purpose agent at the database and hoping.

The second constraint was longevity. Real work on real systems is not a single prompt; it is a session that spans hours, inspects a system, holds a database connection open, tries something, checks the result, and continues. Most agent tooling treats each call as stateless, which makes that kind of work impossible. I needed infrastructure that treated the session - not the prompt - as the unit of work.

Technical details

AgentLib: an agent is a class#

Most agent frameworks ask you to describe your tools in JSON schemas, wire up routing machinery, and learn their mental model of how an agent works. AgentLib starts from a different premise: you already know Python, and a tool is just a method. You write a class, decorate the methods the model is allowed to call, and the signature and docstring become the tool schema. Pydantic validates what comes back. That is the whole idea.

Here is a complete working agent - the kind that answers questions about a SQLite database in production:

Real exampleagentlib-quickstart.py
import sqlite3
from agentlib import BaseAgent

class DatabaseAgent(BaseAgent):
    model = "google/gemini-3.7-flash"
    system = """You answer questions about a SQLite database.
Use execute_query to gather what you need, then call answer."""

    def __init__(self):
        self.db = sqlite3.connect("file:sales.db?mode=ro", uri=True)
        self.db.row_factory = sqlite3.Row

    @BaseAgent.tool
    def execute_query(self, sql: str = "The read-only query to run"):
        # The decorator turns this method into a tool the model can call.
        # The guardrail is ordinary Python - the model cannot argue with it.
        if sql.lstrip().split(None, 1)[0].upper() not in {"SELECT", "WITH"}:
            raise ValueError("Only read-only queries are allowed")
        return [dict(row) for row in self.db.execute(sql)]

    @BaseAgent.tool
    def answer(self, response: str = "The final answer"):
        self.respond(response)

agent = DatabaseAgent()
print(agent.run("Which five customers spent the most this year?"))
A complete, working AgentLib agent. The model sees two tools; everything else - the connection, the guardrails, the loop - is plain Python.

Because an agent is just a class, composition is just Python. Mixins add capabilities when you want them - a shell, a Python environment, MCP servers, file attachments - and the core stays small enough to hold in your head. Even now, you will be hard pressed to find a library that expresses agents as simply and intuitively.

Code Agent: the REPL is the conversation#

Code Agent is an interactive coding agent in the same family as Claude Code or Codex - but built around a Python REPL. The difference from most coding agents is simple to say and changes everything in practice: the agent writes Python for everything instead of making tool calls. Reading a file, running a test, querying a database, editing source - all of it is code the agent writes and runs, in a REPL that stays alive between turns. The REPL is enriched with extra functions and commands, and everything the agent types is validated, healed, and transformed before it runs.

The bigger idea is what happens to the conversation itself. Most agents treat context as an immutable ledger that only grows - until it gets compacted, and the details you needed evaporate. Code Agent's conversation state evolves and mutates instead, and the agent manages its own context:

  • Attachments keep files fresh. Exactly one copy of each file lives in context. After an edit, the old version is redacted and the new version attached - and from the agent's perspective, it did that itself. No re-reading files, no dancing around an edit, no grepping the same lines twice.
  • Ephemeral context handles the small stuff. Notices like the current attachment list, or a reminder to record a note, reach the agent without entering the durable conversation. Those notes later become boundaries for rollups.
  • Rollups replace compaction. The agent summarizes finished stretches of work itself, at natural boundaries, while the summaries stay expandable. Combined, these unlock effectively infinite context without the performance degradation that comes with compaction.

It also pairs naturally with subagents. An orchestrator can hand a long task to a single subagent and go back and forth with it - reviewing its code, keeping it focused - or let one become a specialist that owns data retrieval or browser control.

None of this is theoretical. I have had productive coding sessions run past 50,000 turns, and I regularly retrieve an old conversation to keep working on something because it already has everything in context. There is no degradation, because I set a 200k context constraint even on models that support far more: the constraint pushes the agent to manage its context below the limit, it does not break if the limit is briefly exceeded, and in practice the agents are very good at staying under it.

How they fit the rest of the business#

The pricing agents, the email triage, the browser automation, the day-to-day development of the commerce platform - all of it runs on these two projects. The loop feeds itself: real problems keep shaping the tools, and the tools keep making the next system cheaper to build. That is also why neither project tries to be clever. An agent you can read is an agent you can debug at 2 a.m., and a tool schema that is just a Python signature is one a new teammate already understands. The best compliment either project gets is that people pick it up quickly and stop thinking about it.

The retrospective#

What held up: staying close to Python and close to the provider APIs. The abstractions that were thin have aged well; every time I was tempted to add orchestration machinery, the simpler version won. What I would do earlier: the context-management layer. It was built in response to sessions that collapsed under their own weight, and the work lost before it existed is the strongest argument for having built it first.

The through-line of this whole site shows up here too. The discipline that keeps a double-entry ledger accurate and a pricing agent bounded is the same discipline that makes an agent runtime trustworthy - understand the operation, bound what must never go wrong, keep state durable, and make failure recoverable. The capability is new. The engineering is not.