Add agentic harness integration: types, HarnessEnvironment, OpenClaw adapter (RFC 005)#389
Add agentic harness integration: types, HarnessEnvironment, OpenClaw adapter (RFC 005)#389Darktex wants to merge 8 commits into
Conversation
Greptile SummaryThis PR implements PR 2 from RFC 005's implementation plan: the foundation type system for agentic harness integration. It introduces Pydantic models ( The changes are additive and self-contained — no existing code is modified. The types align well with the project's existing Pydantic-based type system and follow the same
Alignment Review ReportAutomated Checks
Tier 1: Fixes Required
Tier 2: Alignment DiscussionNone identified — the types are purely additive, respect the dual API boundary (harness messages go through Confidence Score: 4/5
Important Files Changed
Class DiagramclassDiagram
class Action {
+Dict metadata
}
class HarnessAction {
+Literal type = "harness_message"
+str message
}
Action <|-- HarnessAction
class HarnessTransport {
<<enum>>
STDIO = "stdio"
STREAMABLE_HTTP = "http"
MCP = "mcp"
}
class HarnessEventType {
<<enum>>
LLM_REQUEST
LLM_RESPONSE
LLM_CHUNK
TOOL_CALL
TOOL_RESULT
TEXT_OUTPUT
ERROR
TURN_COMPLETE
}
class HarnessEvent {
+HarnessEventType type
+float timestamp
+Dict data
}
HarnessEvent --> HarnessEventType
class HarnessResponse {
+str response
+List~HarnessEvent~ events
+bool done
}
HarnessResponse --> HarnessEvent
class HarnessConfig {
+str name
+List~str~ command
+str working_directory
+Dict env_vars
+HarnessTransport transport
+Optional~str~ mcp_config_path
+float startup_timeout_s
+float session_timeout_s
+Optional~str~ model
+Optional~str~ api_key_env_var
}
HarnessConfig --> HarnessTransport
class HarnessAdapter {
<<abstract>>
+HarnessConfig config
+start(working_directory) None
+stop() None
+inject_tools(tools) None
+send_message(message) HarnessResponse
+send_message_streaming(message) AsyncIterator~HarnessEvent~
+is_alive() bool
}
HarnessAdapter --> HarnessConfig
HarnessAdapter --> HarnessResponse
HarnessAdapter --> HarnessEvent
class Tool {
+str name
+str description
+Dict input_schema
}
class resolve_tool_conflicts {
<<function>>
+resolve(env_tools, harness_builtin_tools) List~Tool~
}
resolve_tool_conflicts --> Tool
Last reviewed commit: 0c5d074 |
a993c0c to
2408704
Compare
Introduces the core type system for agentic harness integration: - HarnessConfig: Pydantic model for harness process configuration - HarnessTransport: Enum for communication transport (stdio, http, mcp) - HarnessAdapter: ABC for harness-specific lifecycle management - HarnessEvent/HarnessEventType: Standard event schema for trajectories - HarnessResponse: Complete turn response with events and done signal - HarnessAction: Action type for sending messages to harnesses - resolve_tool_conflicts(): Utility for tool name collision handling 43 tests covering all types, serialization, validation, and edge cases. Part of #385
- Add List[Tool] type parameter to HarnessAdapter.inject_tools() - Add gt=0 validation to timeout fields in HarnessConfig
Implements HarnessEnvironment, which wraps an external agentic harness with OpenEnv's Gym-style API: - reset() stops any running harness, injects MCP tools, starts fresh - step(HarnessAction) sends one conversational turn to the harness - Harness maintains conversation context across step() calls - done signal propagated from harness to observation - Trajectory accumulated across turns, accessible via .trajectory - close() cleans up harness process 26 tests covering reset, multi-turn step, trajectory accumulation, state management, close behavior, and MCP tool injection. Part of #385
- Fix broken async context manager in _get_mcp_tool_definitions: use single async function with 'async with' instead of separate run_async_safely calls for __aenter__/__aexit__ - Add warning log on MCP tool extraction failure instead of silently swallowing exceptions
Concrete HarnessAdapter for the OpenClaw agentic platform: - Process lifecycle: start/stop via asyncio subprocess - MCP tool injection: writes mcpServers config to openclaw.json, merges with existing config entries - Communication: JSON-line protocol over stdin/stdout - Event extraction: parses tool_calls from responses into HarnessEvents - Streaming: yields events from turn response - Error handling: timeout detection, plain-text fallback 18 tests covering imports, config injection, process lifecycle, message sending with JSON/plain-text responses, tool call extraction, streaming, and HarnessEnvironment integration. Part of #385
- Fix critical bug: env vars now merge with parent env (os.environ) instead of replacing it, preserving PATH, PYTHONPATH, etc. - When no env_vars or api_key configured, pass None to inherit parent - Add test for parent env inheritance with custom env vars - Add test for None env when no overrides configured - Add test for kill path when terminate times out - Add test for send_message timeout handling - Add test for corrupted config file handling - Add subprocess pipe parameter assertions to start test
Run usort format on all harness source and test files to match the new arc f formatting pipeline added in #412.
a6e4a4f to
a76add8
Compare
|
will this be merged ? |
Summary
Implements the full agentic harness integration from RFC 005 (#387). This adds support for wrapping external harnesses (OpenClaw, Claude Code, Gemini CLI, etc.) that own their own ReAct control loop.
What's included
Foundation types (
src/openenv/core/harnesses/types.py):HarnessConfig— Pydantic model for harness process configurationHarnessTransport— Enum: stdio, streamable HTTP, MCPHarnessEvent/HarnessEventType— Standard event schema for turn trajectories (8 event types)HarnessResponse— Complete turn response with events and done signalHarnessAction— Action type for sending messages to harnessesAdapter ABC (
src/openenv/core/harnesses/adapter.py):HarnessAdapter— Abstract base for harness-specific lifecycle (start, stop, inject_tools, send_message, send_message_streaming)HarnessEnvironment (
src/openenv/core/harnesses/environment.py):reset()starts fresh harness process and conversationstep(HarnessAction)sends one conversational turn, harness does ReAct loopstep()calls.trajectorypropertyOpenClaw adapter (
src/openenv/core/harnesses/adapters/openclaw.py):.openclaw/openclaw.jsonconfigUtilities (
src/openenv/core/harnesses/tools.py):resolve_tool_conflicts()— Tool name collision handling viaenv_prefixingTest plan
Closes #385