Metadata-Version: 2.4
Name: crashoverride
Version: 0.2.0
Summary: A minimal agentic exception handler
Project-URL: Repository, https://bitbucket.org/njatkinson/crashoverride
Author: CrashOverride contributors
License:             DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
                            Version 2, December 2004
        
         Copyright (C) 2026 CrashOverride contributors
        
         Everyone is permitted to copy and distribute verbatim or modified
         copies of this license document, and changing it is allowed as long
         as the name is changed.
        
                    DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
           TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
        
          0. You just DO WHAT THE FUCK YOU WANT TO.
License-File: LICENSE
Classifier: License :: Freely Distributable
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Requires-Python: >=3.11
Requires-Dist: openai-agents
Provides-Extra: dev
Requires-Dist: build; extra == 'dev'
Requires-Dist: pytest; extra == 'dev'
Requires-Dist: twine; extra == 'dev'
Description-Content-Type: text/markdown

# CrashOverride

CrashOverride lets a Python application modify its own behavior when its
existing code cannot complete an operation.

An exception transfers control to an AI agent that can inspect the traceback
and live process, infer the intended postcondition, implement missing behavior,
and continue. This enables recovery, adaptation, and **just-in-time
implementation** rather than merely retrying predefined code.

## Install

CrashOverride supports Python 3.11 and newer. Python 3.13+ is recommended: its
write-through frame locals let the agent restore caller variables directly.
Earlier versions retain the other recovery and self-modification capabilities.

```console
pip install crashoverride
export OPENAI_API_KEY=...
```

## Quick start

Create one application-wide recipe, then protect meaningful units of work:

```python
from crashoverride import CrashOverride


def extract_article(payload):
    return payload["article_text"]


crash_override = CrashOverride(
    project_prompt="""
    This is a nightly scraper. Article text must come from the source response;
    never invent it.
    """,
)

for url in urls:
    with crash_override.handler(
        prompt="""
        Leave `article` containing trustworthy text. If the response format
        changed, repair the extractor for this and subsequent records.
        Re-raise the exception if the article cannot be recovered.
        """,
    ):
        payload = download(url)
        article = extract_article(payload)

    save(article)
```

If the site renamed `article_text` to `body`, the agent can inspect `payload`,
patch `extract_article` in the running process, recover the current article,
and keep the scrape alive.

## Recovery model

When a protected block raises a matching exception, CrashOverride creates a
fresh OpenAI Agents SDK agent with:

- The application context in `project_prompt`.
- The handler's recovery objective.
- The exception, traceback, and tools connected to the live process.
- Any application tools or MCP servers you supplied.

The agent can inspect and mutate objects, globals, caller locals, functions,
classes, modules, and other process state. It can also reconcile partial side
effects: for example, checking whether an upload reached the server before a
connection failed, then reconstructing the result instead of creating a
duplicate.

Execution resumes **after the entire `with` block**, not at the failed
statement. Write handler prompts as postconditions:

- “Leave `record` containing a trustworthy decoded record.”
- “Ensure the form was submitted exactly once and recover its confirmation
  number.”
- “Restore the parser so the current and remaining files can be processed.”

If the agent completes normally, CrashOverride suppresses the exception. If it
cannot establish the requested state, it can re-raise the original failure.

No client, agent, or inference request is created when the block succeeds.
Recovery resources exist only for one matching exception.

## Beyond the current exception

A recovery can monkeypatch a persistent process or fill a deliberate
`NotImplementedError` only when reached. When the project or handler prompt
authorizes source changes, the agent can make that runtime repair durable. This
is particularly powerful for CGI applications and scripts: recover the current
invocation, update its source, and the next invocation automatically loads the
new implementation.

The agent can launch a thread, subprocess, async task, or durable job so
follow-up work does not block application flow. Custom tools can connect it to
a coding harness, enqueue a permanent fix, open a ticket with the traceback and
diagnosis, or fire an alert. A live failure becomes both an immediate repair
and an evidence-rich handoff.

## Configuration

Application settings are inherited by every handler. A handler may override
the model, credentials, or caught exceptions, and may add tools or MCP servers:

```python
crash_override = CrashOverride(
    project_prompt="Database writes must be idempotent.",
    model="the-model-to-use",
    catch=(ValueError, OSError),
    tools=(inspect_remote_service,),
    mcp_servers=(operations_server,),
)

with crash_override.handler(
    prompt="Leave `invoice` referring to the persisted invoice.",
    tools=(read_invoice_audit_log,),
):
    invoice = create_invoice(request)
```

Handlers catch `Exception` by default, and the Agents SDK selects its default
model. Credentials are read only if recovery is triggered.

`system_prompt`, `openai_client_factory`, and `agent_factory` are advanced
escape hatches. Prefer `project_prompt` for application-wide facts and the
handler `prompt` for the local objective.

### Tools and MCP servers

The agent already has arbitrary Python execution. Custom tools and MCP servers
provide discoverable interfaces to audit logs, services, coding harnesses,
incident systems, and durable job queues.

Tools and MCP servers are affordances, not a security boundary. Application
tools are available to every recovery; handler tools apply only to that block.
Their presence does not authorize source changes; grant that permission in
`project_prompt` or the handler prompt.

### Async programs

Use `async with` inside an event loop:

```python
async with crash_override.handler(
    prompt="Leave `payload` containing a valid response.",
):
    payload = await fetch_payload()
```

Recovery tools support top-level `await`. Use regular `with` in synchronous
programs.

## Security

CrashOverride deliberately gives the model arbitrary Python execution with the
permissions of your process. It can read secrets, modify memory, write files,
make network requests, change source code, expose data, or corrupt the process.

There is no sandbox, approval step, rollback mechanism, or transaction
boundary. Use CrashOverride only where those capabilities are acceptable. If
restarting from a clean state is safer than repairing the live process, restart
it.

For implementation details, read
[`crashoverride/_runtime.py`](https://bitbucket.org/njatkinson/crashoverride/src/main/crashoverride/_runtime.py).
