OpenBox governance SDK for Agent-RT agents. Wraps Agent-RT's
public seams (ModelProvider, ToolRegistry / ToolExecutor, AgentLoop) and enforces OpenBox
policies, guardrails, and HITL approvals inline. Mirrors the layout and conventions of
openbox-langchain-sdk-python; the TypeScript twin is openbox-agentrt-sdk-ts.
Key principle: only the Agent-RT integration layer lives here. All governance infrastructure
(client, gate, runtime, approvals, instrumentation, contracts) is imported from openbox-sdk-python
(openbox_core) — never copied.
- Layer 1:
OpenBoxAgentRTGovernance— run scope + governed provider/tool/loop wrappers. - Layer 2: HTTP/DB/file hook governance —
openbox_coreinstrumentation, installed per runtime. - Layer 3: activity context mapping — wrappers bind
activity_scope(...)to the runtime's ownContextStorearound every model and tool body so Layer-2 spans resolve to the right activity.
Run identity lives in a ContextVar (run_state.py) bound by run_scope, never on the
governance instance, so concurrent runs never cross-contaminate.
Invariants:
- Agent-RT is async-only: every gate uses
runtime.aevaluate_lifecycle; do not add a sync path. - Start-stage events are enforced (
gov.gate); completion events are telemetry only (gov.telemetry), best-effort, and never raise — not for a verdict, and not for a transport or auth failure underfail_closed(the work already ran; a failed send must not discard its result). - On any enforcement failure: close the orphan start row (same activity id — model, tool, and
pre-screen rows alike), send
WorkflowFailedexactly once (RunState.closed), then re-raise. - No orphan rows: model/tool wrappers and
run_scopehandleBaseException, so cancellation by anAgentLooprun deadline or cancellation token still recordsActivityCompleted/WorkflowFailed. An allowed pre-screen that no model call consumed is closed at run end, beforeWorkflowFailedwhen a later gate blocks (close_run). - The run's first model call for the pre-screened prompt (
RunState.pre_screen_prompt) reuses the pre-screen verdict (RunState.pre_screen_used) — even when the caller passes prior chat history. - Streaming: bind
activity_scopearound each__anext__step, never across ayield(AgentLoopruns each step in its own task under a deadline; aContextVarreset from another context raises).GovernedModelProvidermust never delegatestreamto the ungoverned inner provider. - One
EvaluationClientper runtime, shared with theApprovalPoller(runtime_builder.build_client), so approval polls carry every identity mode and close with the runtime. AgentLooppreferstool_registry.executeovertool_executor.execute; wrap both.- Redaction builds new frozen
ModelMessages; never mutate conversation history. - Keep every source file under 240 lines (the LangChain reference limit); format with
ruff format(one argument per line, as in the reference).
openbox_agentrt/
├── __init__.py # Public API
├── factory.py # create_openbox_agentrt() (async; validates the API key)
├── governance.py # OpenBoxAgentRTGovernance: run_scope, gate, telemetry, wrappers
├── options.py # OpenBoxAgentRTOptions
├── runtime_builder.py # config resolution + one client → OpenBoxRuntime (+ ApprovalPoller)
├── run_state.py # RunState + ContextVar
├── model_provider.py # GovernedModelProvider (+ streaming variant)
├── tool_governance.py # GovernedToolRegistry / GovernedToolExecutor
├── loop.py # GovernedAgentLoop
├── lifecycle_events.py # envelope builders, error_info, __openbox enrichment
├── message_extraction.py # ModelMessage helpers, redaction, response metadata
├── sdk_metadata.py # static SDK identity (engine "agentrt")
└── py.typed # PEP 561 marker: ship inline types to consumers
OpenBoxAgentRTGovernance— Owns oneOpenBoxRuntime; run scope, gate, telemetry, seam wrappersOpenBoxAgentRTOptions— Configuration dataclassGovernedAgentLoop— GovernedAgentLoop(run,run_streaming)GovernedModelProvider/GovernedStreamingModelProvider— GovernedModelProviderGovernedToolRegistry/GovernedToolExecutor— Governed tool seamsRunState— Per-run identity, pre-screen state, and close flag (bound in aContextVar)
create_openbox_agentrt()— Async factory (primary entry point); validates the API keyOpenBoxAgentRTGovernance.run_scope()—WorkflowStarted,SignalReceived, pre-screen, run closeOpenBoxAgentRTGovernance.govern_loop()— Wraps anAgentLoop's provider, registry, and executorOpenBoxAgentRTGovernance.gate()— Enforced start-stage evaluation; closes orphans on failureOpenBoxAgentRTGovernance.telemetry()— Best-effort completion send; never raisesgovern_tool_call()— Tool gate,activity_scope, and completion for registry/executor pathsbuild_runtime()— Config resolution, one shared client, optionalApprovalPoller
from agent_rt import AgentLoop, ToolRegistry, load_model
from openbox_agentrt import create_openbox_agentrt
# Create governance
governance = await create_openbox_agentrt(
api_url="https://core.openbox.ai",
api_key="obx_live_...",
agent_name="MyAgent",
)
# Wrap an Agent-RT loop
loop = governance.govern_loop(AgentLoop(load_model(), tool_registry=ToolRegistry()))
# Run — governance applied automatically
result = await loop.run(agent, messages)
await governance.aclose()# Install
pip install -e ".[dev]"
# Test
pytest tests/ -v
# Lint + format
ruff check openbox_agentrt/ tests/ examples/
ruff format openbox_agentrt/ tests/ examples/
# Type check
mypy openbox_agentrt/Current: 47 tests, 100% pass rate, 94.67% package coverage
Coverage:
- Full governed
AgentLooplifecycle against a real Agent-RT loop - Verdict enforcement (BLOCK at signal/pre-screen/model/tool, REQUIRE_APPROVAL fail-safe, guardrail redaction)
- Orphan-row closure, cancellation, streaming deadlines, telemetry failures
- Pre-screen reuse, factory option validation (
validate,skip_tool_types,send_*_event), message extraction - Package boundaries (imports limited to Agent-RT + base SDK) and the Agent-RT 0.0.3 pin
Offline only: tests wire OpenBoxRuntime to openbox_core.conformance.fake_core.FakeCore
(queued verdicts + payload capture) — no network, no real LLM. Queue order matters: every
telemetry send (incl. WorkflowStarted and LLMCompleted) consumes one queued response.
tests/test_lifecycle_hardening.py covers the orphan-row, cancellation, streaming-deadline,
telemetry-failure, and pre-screen-reuse invariants above; keep it in lockstep with the TS
test/lifecycle-hardening.test.ts.
Require exactly Agent-RT 0.0.3 for optional Traceloop packaging and the verified public
integration seams. Pin the content-builder example to the same exact version and maintain
both SDK and example uv.lock files. Keep the supported Agent-RT version in sync with the
TypeScript SDK and re-run offline tests when updating it.
- README.md — install, quick start, configuration
- docs/system-architecture.md — data flow and verdict handling
- docs/code-standards.md — implementation/test conventions
- docs/codebase-summary.md — module ownership and boundaries
- docs/project-overview-pdr.md — requirements and acceptance criteria
- docs/project-roadmap.md — release verification, risks, and follow-up work
- examples/content-builder-agent/ — governed port of the LangChain reference example (Tavily search, Gemini images), aligned with the TS twin
Preserve the exact five-document docs/ layout of the LangChain Python reference.
Match its Markdown structure (title-cased headings, detailed patterns/requirements,
architecture flows, tables, release checks, and cross-links) while describing only
Agent-RT's verified behavior. Keep file paths and measured status claims current.
- Agent-RT:
../python(runtime) and../docs/Introduction-to-Harness.md - LangChain reference SDK:
../openbox-examples/openbox-langchain-sdk-python