> ## Documentation Index
> Fetch the complete documentation index at: https://opensre.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Python API

> Drive the OpenSRE agent in-process from your own Python code

Use this when your Python code runs on the same machine as OpenSRE and you want
agent answers without shelling out to the CLI. To call OpenSRE over the network
instead, use the [HTTP API](/docs/api).

## Prerequisites

The standalone CLI installer does not expose an importable package, so embed
from a source checkout:

```bash theme={null}
git clone https://github.com/Tracer-Cloud/opensre.git
cd opensre && make install
```

Configure a provider once (`opensre onboard`) — the harness reuses the same
config and credentials as the CLI. Run your script inside the checkout's
environment with `uv run python your_script.py`.

## One turn

```python theme={null}
from core.agent_harness import AgentHarness

harness = AgentHarness.start()
result = harness.dispatch_message("why is checkout-api slow?")
if result.answered:
    print(result.primary_response_text)
```

`AgentHarness.start()` resolves the environment, opens a session, and attaches
an agent with the standard ports — the same tools and prompts the interactive
shell uses.

Always check `result.answered` before trusting the text: when a turn fails (for
example the LLM provider is unreachable), the error message itself lands in
`result.primary_response_text`.

## A conversation

Each `dispatch_message` call is one turn in the same session, so follow-ups see
earlier context:

```python theme={null}
harness.dispatch_message("list unresolved Sentry issues from the last 24 hours")
result = harness.dispatch_message("which of those affect checkout?")
```

To resume a previous session instead of starting a new one, pass its ID:

```python theme={null}
from core.agent_harness import AgentHarness, HarnessConfig

harness = AgentHarness.start(HarnessConfig(session_id="abc123"))
```

## Your own grounding context

The agent builds its prompts from the session by default. To ground it your own
way — a different system prompt, your own retrieved context — pass a provider:

```python theme={null}
from core.agent_harness import AgentHarness, HarnessConfig

harness = AgentHarness.start(HarnessConfig(prompts=my_provider))
```

Omit it and the built-in provider applies
(`core.agent_harness.DefaultPromptContextProvider`). A custom provider must
satisfy `core.agent_harness.ports.PromptContextProvider`. The same `prompts=`
argument is accepted by `build_default_headless_agent` on the custom-ports path
below.

## Custom output and ports

`start()` buffers all output. To capture tool progress yourself — stream to a
websocket, collect for a report — build the agent explicitly and pass your own
sink:

```python theme={null}
from core.agent_harness import (
    AgentHarness,
    BufferOutputSink,
    HarnessConfig,
    build_default_headless_agent,
)

harness = AgentHarness(HarnessConfig())
startup = harness.startup()
sink = BufferOutputSink()
agent = build_default_headless_agent(
    session=startup.session,
    output=sink,
    prompts=my_provider,  # optional — same port as HarnessConfig.prompts
)
harness.attach_agent(agent)

harness.dispatch_message("summarize open incidents")
print(sink.lines)      # rendered output lines
print(sink.streamed)   # streamed answer chunks
```

Any object implementing the `OutputSink` protocol
(`core.agent_harness.ports.OutputSink`: `print`, `render_response_header`,
`render_error`, `stream`) works in place of `BufferOutputSink`.
`build_default_headless_agent` also accepts a custom logger, prompt surface,
and tool observers — see its docstring for the full port list.

## Your own tools

Register a package before the first tool lookup:

```python theme={null}
from tools.registry import register_external_tool_package
import my_agent_tools

register_external_tool_package(my_agent_tools)
```

Two gotchas:

1. Declare tools with `surfaces=("action",)` (or include `"action"`). The
   `@tool` default is `("investigation",)` only — the chat/Python-API action
   loop will not see investigation-only tools.
2. Tools may live in the package's `__init__.py` or in submodules; both are
   scanned after registration.
