OpenHarness: Open-Source Python Agent Harness with Multi-Provider Support & ohmo Personal Agent

code HKUDS-OpenHarness
agentpythontool-usemulti-agentmcppermissions

HKUDS-OpenHarness — L2 #

§1 TL;DR #

Open-source Python agent harness (~3M chars) reimplementing Claude Code's architecture with multi-provider support (Anthropic/OpenAI/Copilot/Gemini/Ollama), 43 tools, Pydantic-based tool system, plugin/skill/hook extensibility, multi-agent swarm coordination, and an opinionated personal agent (ohmo) with Telegram/Slack/Discord/Feishu integration.

§2 Project Identity #

FieldValue
Repo
Primary languagePython (2.97M bytes), TypeScript (140K bytes)
Total chars~3.1M across Python + TypeScript + Shell
LicenseMIT
Stars13,112
Version analyzedv0.1.9
Maintainer / OrgHKUDS (HKU Data Science Lab)
Created2026-04-01
Date read2026-05-26
CLI entry pointsoh / openharness / openh (harness), ohmo (personal agent)
Packageopenharness-ai (PyPI)
Build systemhatchling

§3 Motivation & Core Questions #

One-line pitch: Open-source Python reimplementation of Claude Code's agent harness with first-class multi-provider support, enabling researchers and builders to run the same agentic loop against any LLM backend.

Q1 痛点:Claude Code is powerful but closed, single-provider, and not extensible by the community #

Claude Code ships as a compiled TypeScript binary tied to Anthropic's API. Three pain points motivate a reimplementation: (1) Closed source — the decompiled TypeScript is research-only; no one can legally extend or redistribute it. (2) Single-provider lock-in — Claude Code works only with Anthropic's Claude; users with OpenAI, DeepSeek, Gemini, or local Ollama deployments are excluded. (3) No personal agent story — Claude Code is a session-scoped CLI tool; it has no mechanism for persistent, always-on agent presence across chat platforms. A community-maintainable Python reimplementation with provider abstraction and a personal agent layer addresses all three gaps.

Q2 方法:Provider-abstracted QueryEngine + Pydantic tool system + ohmo personal agent layer #

OpenHarness factors the agent into three layers:

  1. QueryEngine (core loop): Streaming tool-call cycle with API retry, token counting, cost tracking, auto-compaction, and session memory. Provider-agnostic — any client implementing SupportsStreamingMessages works.
  2. Tool system (43 tools): Each tool is a BaseTool subclass with a Pydantic input_model for validation, execute() for logic, and is_read_only() for permission classification. The ToolRegistry maps names → implementations and generates JSON Schema for the API.
  3. ohmo (personal agent): A separate product built atop the harness — persistent identity (soul/, identity/), multi-channel gateway (Telegram, Slack, Discord, Feishu), and workspace at ~/.ohmo/. Runs on existing Claude Code or Codex subscriptions.
  4. The multi-provider abstraction lives in src/openharness/api/ with separate clients for Anthropic-compatible, OpenAI-compatible, and GitHub Copilot APIs. Provider profiles are configured via oh setup or oh provider edit.

    核心技术壁垒 #

    The provider-abstracted streaming tool-use loop built on Python's AsyncIterator[StreamEvent]. The QueryEngine.submit_message() method returns an AsyncIterator that yields StreamEvent objects as they arrive from any backend (Anthropic, OpenAI, Copilot, Gemini, Ollama). This single abstraction makes every downstream consumer — the CLI, the React TUI, the ohmo gateway, and the SDK — work identically regardless of which LLM provider backs the session. The hard part is not the abstraction itself but maintaining streaming fidelity across providers with incompatible SSE formats: Anthropic uses content_block_delta events with explicit tool_use blocks, OpenAI uses function_call chunks within choices[0].delta, and Copilot requires device-flow OAuth before any streaming begins. Normalizing these into a single StreamEvent type while preserving backpressure, cancellation, and partial-result semantics is the core engineering challenge.

    Q3 结果 #

    13K+ stars in under 2 months (2026-04-01 → 2026-05-26). 114 unit/integration tests passing, 6 real-model E2E tests, 9 harness feature E2E tests, 12 skill/plugin compatibility tests. Supports 10+ provider backends out of the box. The ohmo personal agent adds a novel product surface absent from all comparable agent CLIs.

    §4 Architecture & Module Map #

    flowchart TB subgraph ENTRY["Entry Layer"] CLI["cli.py (91KB)
    typer app: oh / openh / openharness"] OHMO["ohmo/cli.py
    ohmo personal agent CLI"] TUI["frontend/terminal/
    React/Ink TUI (TypeScript)"] end subgraph ENGINE["Query Engine Core"] QE["engine/query_engine.py
    QueryEngine: submit_message() → AsyncIterator‹StreamEvent›"] LOOP["engine/ internals
    run_query() while-true loop"] STREAM["engine/ streaming
    stream processing, cost tracking"] end subgraph API["API Clients"] ANTH["api/ anthropic-compat
    Claude, Kimi, GLM, MiniMax"] OAI["api/ openai-compat
    OpenAI, OpenRouter, DeepSeek, Ollama, Gemini"] COPILOT["api/ copilot
    GitHub Copilot device-flow"] end subgraph TOOLS["Tool System (43)"] BASE["tools/base.py
    BaseTool + ToolRegistry + ToolResult"] subgraph TCAT["Tool Categories"] TFILE["File I/O: Bash, Read, Write, Edit, Glob, Grep"] TSEARCH["Search: WebFetch, WebSearch, ToolSearch, LSP"] TNOTE["Notebook: NotebookEdit"] TAGENT["Agent: Agent, SendMessage, TeamCreate/Delete"] TTASK["Task: Create/Get/List/Update/Stop/Output"] TMCP["MCP: MCPTool, ListMcpResources, ReadMcpResource"] TMODE["Mode: EnterPlanMode, ExitPlanMode, Worktree"] TSCHED["Schedule: CronCreate/List/Delete, RemoteTrigger"] TMETA["Meta: Skill, Config, Brief, Sleep, AskUser"] end end subgraph HARNESS["Harness Mechanisms"] PERM["permissions/
    default / auto / plan modes"] HOOKS["hooks/
    PreToolUse / PostToolUse lifecycle"] MEMORY["memory/
    MEMORY.md, session memory, durable extraction"] SKILLS["skills/
    on-demand .md loading"] PLUGINS["plugins/
    commands + hooks + agents + MCP"] PROMPTS["prompts/
    system prompt assembly, CLAUDE.md injection"] CONFIG["config/
    multi-layer config, provider profiles, migrations"] COORD["coordinator/
    subagent spawning, team registry"] SWARM["swarm/
    multi-agent swarm coordination"] SANDBOX["sandbox/
    native + Docker backend"] MCP_C["mcp/
    MCP client, HTTP transport, auto-reconnect"] SERVICES["services/
    autodream, session memory, memory extraction"] end subgraph CHANNELS["ohmo Channels"] TG["channels/telegram"] SL["channels/slack"] DC["channels/discord"] FS["channels/feishu"] end CLI --> QE OHMO --> QE TUI --> QE QE --> LOOP LOOP --> STREAM LOOP --> ANTH LOOP --> OAI LOOP --> COPILOT LOOP --> BASE BASE --> TCAT BASE --> PERM PERM --> HOOKS QE --> MEMORY QE --> PROMPTS QE --> CONFIG QE --> SKILLS QE --> PLUGINS QE --> COORD COORD --> SWARM QE --> MCP_C QE --> SANDBOX QE --> SERVICES OHMO --> TG OHMO --> SL OHMO --> DC OHMO --> FS

    Top modules by centrality:

    ModulePurpose
    src/openharness/engine/Agent loop core: streaming tool-call cycle, API retry, token counting, cost tracking
    src/openharness/tools/base.pyBaseTool ABC + ToolRegistry + ToolResult — the tool contract
    src/openharness/cli.py (91KB)Primary CLI entry point — typer app with all flags, subcommands, session mgmt
    src/openharness/api/Multi-provider API clients: Anthropic-compat, OpenAI-compat, Copilot
    src/openharness/permissions/3-mode permission system: default (ask), auto (allow), plan (block writes)
    src/openharness/hooks/PreToolUse / PostToolUse lifecycle event hooks
    src/openharness/commands/54 slash commands
    src/openharness/coordinator/Subagent spawning, team registry, task delegation
    src/openharness/memory/Persistent cross-session memory: MEMORY.md, session memory, durable extraction
    src/openharness/mcp/MCP client with HTTP transport and auto-reconnect
    src/openharness/plugins/Plugin ecosystem: commands, hooks, agents, MCP server plugins
    ohmo/Personal agent: gateway, workspace, soul/identity, multi-channel chat

    §5 Entry Points & API Surface #

    Public Entry Points #

    EntryPathRole
    CLI (harness)src/openharness/cli.pyoh / openharness / openh — primary user-facing CLI
    CLI (ohmo)ohmo/cli.pyohmo — personal agent CLI (init, config, gateway)
    React TUIfrontend/terminal/TypeScript/Ink terminal UI
    Python APIfrom openharnessProgrammatic SDK access

    CLI Flags (top 10) #

    FlagPurpose
    -c / --continueResume last session
    -r / --resumeResume specific session by ID
    -n / --nameName a session
    -m / --modelSelect model
    --effortSet model effort level
    --max-turnsLimit agent loop iterations
    -p / --printNon-interactive print mode
    --output-formattext / json / stream-json
    --permission-modedefault / auto / plan
    --dangerously-skip-permissionsBypass all permission checks

    Subcommands #

    oh setup (provider config), oh provider (manage backends), oh auth (credential mgmt), oh mcp (MCP server config), oh plugin (plugin mgmt).

    Extension Points #

    • Tools: Subclass BaseTool, define name, description, input_model, implement execute() — register via ToolRegistry.register()
    • Plugins: Drop-in BaseTool subclasses in plugin tools/ directories — auto-discovered at runtime
    • Skills: .md files in bundled/user/project/plugin skill directories — loaded on demand via Skill tool
    • Hooks: PreToolUse / PostToolUse lifecycle scripts
    • Commands: Slash command extensions (54 built-in)
    • MCP: External tool servers via Model Context Protocol
    • Providers: Any backend implementing SupportsStreamingMessages protocol

    Provider Profiles #

    ProfileBackends
    Anthropic-compatibleClaude official, Kimi, GLM, MiniMax
    Claude subscription~/.claude/.credentials.json (local credential reuse)
    OpenAI-compatibleOpenAI, OpenRouter, DashScope, DeepSeek, SiliconFlow, Groq, Ollama, GitHub Models, NVIDIA NIM, Gemini
    Codex subscription~/.codex/auth.json
    GitHub CopilotDevice-flow OAuth login

    §6 Core Data Structures #

    BaseTool — Tool Contract (src/openharness/tools/base.py) #

    • Layout: ABC with class-level name: str, description: str, input_model: type[BaseModel]. Three methods: execute() (async, the tool logic), is_read_only() (permission hint), to_api_schema() (JSON Schema for API).
    • Lifecycle: Instantiated at startup, registered in ToolRegistry. Stateless — all mutable state flows through ToolExecutionContext.
    • Mutation: Immutable after construction. Tool authors define behavior via execute() override.

    ToolResult — Execution Output (src/openharness/tools/base.py) #

    • Layout: Frozen dataclass with output: str, is_error: bool, metadata: dict[str, Any].
    • Lifecycle: Created per tool invocation, consumed by the agent loop to build the tool_result message.
    • Mutation: Immutable (frozen dataclass).

    ToolExecutionContext — Invocation Environment (src/openharness/tools/base.py) #

    • Layout: Dataclass with cwd: Path, metadata: dict[str, Any], hook_executor: HookExecutor | None.
    • Lifecycle: Created per tool invocation from the QueryEngine's current state.
    • Mutation: Mutable — metadata dict can be modified during execution.

    ToolRegistry — Tool Name → Implementation Map (src/openharness/tools/base.py) #

    • Layout: Internal _tools: dict[str, BaseTool].
    • Lifecycle: Created at startup, populated during bootstrap, queried per API call for tool schemas.
    • Mutation: Append-only after initial registration.

    QueryEngine — Conversation + Loop Owner (src/openharness/engine/query_engine.py) #

    • Layout: Holds api_client (any provider), tool_registry, permission_checker, messages[] (conversation history), model config, context window limits, auto-compact thresholds, hooks, settings.
    • Lifecycle: One instance per session. Persists across turns. Session resume loads prior messages.
    • Mutation: Messages list grows monotonically. Model, system prompt, permission checker, effort can be swapped mid-session via setter methods.

    §7 Critical Path Analysis #

    Hot path: User prompt → streamed response #

    sequenceDiagram participant U as User / CLI participant QE as QueryEngine participant SM as submit_message() participant RQ as run_query() [agent loop] participant API as Provider API participant TR as ToolRegistry participant PERM as Permissions + Hooks participant T as Tool.execute() participant MEM as Memory Pipeline U->>QE: submit_message(prompt) QE->>SM: sanitize messages, append user msg SM->>SM: remember_user_goal(), prepare_session_memory() SM->>SM: fire USER_PROMPT_SUBMIT hook SM->>RQ: build QueryContext, start loop loop stop_reason == "tool_use" && turns < max_turns RQ->>API: streaming request (messages, tools, model) API-->>RQ: SSE stream (text_delta, tool_use blocks) alt tool_use blocks present loop for each tool_call RQ->>PERM: permission check (mode: default/auto/plan) alt APPROVED PERM-->>RQ: allow RQ->>TR: registry.get(tool_name) TR-->>RQ: BaseTool instance RQ->>T: tool.execute(args, context) T-->>RQ: ToolResult else DENIED PERM-->>RQ: deny end end RQ->>RQ: append tool_results, loop else end_turn RQ-->>QE: AssistantTurnComplete end QE->>QE: accumulate usage/cost QE-->>U: yield StreamEvent end QE->>MEM: update_session_memory() QE->>MEM: extract_durable_memories() QE->>MEM: schedule_auto_dream() QE-->>U: final StreamEvent

    Latency breakdown per turn #

    HopDominant cost
    Message sanitization + goal tracking~1-5 ms (CPU)
    Hook execution (USER_PROMPT_SUBMIT)~1-50 ms (shell script execution)
    Provider API prefill~100 ms–10 s (network + GPU, proportional to context)
    Provider API decode~50 ms–60 s (token generation, model dependent)
    Permission check (per tool)~0 ms (auto) to unbounded (interactive prompt)
    Tool execution (Bash)~50 ms–300 s (command dependent)
    Tool execution (Read/Edit)~1-10 ms (disk I/O)
    Tool execution (WebFetch/WebSearch)~100 ms–10 s (network)
    Memory pipeline (post-turn)~10-500 ms (session memory update + optional LLM extraction)

    The provider API call dominates wall-clock time. The permission pipeline in default mode is the critical human-in-the-loop bottleneck. The memory pipeline (session memory update, durable extraction, auto-dream scheduling) runs as post-turn cleanup and adds latency only when durable extraction triggers an LLM call.

    L1 claims vs. code reality #

    The README's agent loop pseudocode shows a 6-line while-true pattern. The actual submit_message() implementation adds 50+ lines of orchestration: message sanitization, user goal tracking, session memory preparation, hook execution, coordinator context injection, cost accumulation, session memory update, durable memory extraction, and auto-dream scheduling. The simple loop framing correctly captures the control flow but understates the lifecycle management by an order of magnitude.

    §8 作者証明 #

    無形式化作者証明 — 仅实証。

    OpenHarness is an open-source tool, not a research artifact. No formal proofs exist. Correctness assurances come from:

    1. Pydantic validation: Every tool input is validated against a BaseModel schema before execution — malformed arguments are rejected with structured error messages.
    2. Permission pipeline: 3-mode permission system (default/auto/plan) with path-level and command-level rules enforces safety invariants at runtime.
    3. Hook system: PreToolUse / PostToolUse hooks allow user-defined validation scripts to intercept any tool invocation.
    4. Type hints: Full Python type annotations across the codebase (Pydantic v2, dataclass, ABC).
    5. Test suite: 114 unit/integration tests, 6 CLI E2E tests with real model calls, 9 harness feature E2E tests, 12 skill/plugin compatibility tests.
    6. Frozen dataclass for ToolResult: @dataclass(frozen=True) ensures tool outputs are immutable after creation, preventing mutation bugs in the conversation history pipeline.
    7. The key liveness property — that the agent loop terminates — is enforced by max_turns (configurable, default 8). Without this guard, the loop runs until the model emits a non-tool_use stop reason or the user interrupts.

      §9 Concurrency & Memory #

      Concurrency Model #

      MechanismWhereWhy
      Single-threaded asyncio event loopPython main processAll core logic is async/await; I/O is non-blocking
      AsyncIterator streamingQueryEngine.submit_message() → consumerPull-based streaming with natural backpressure
      Subprocess agentsCoordinator subagent spawningIsolated execution contexts for multi-agent tasks
      Background servicesautodream, session memory, memory extractionPost-turn async processing
      Docker sandboxsandbox.backend = "docker"Full process isolation with resource limits

      Memory Management #

      • Message accumulation: messages[] grows monotonically within a session. No GC of old messages — this drives the need for auto-compaction.
      • Auto-compact: When token count exceeds auto_compact_threshold_tokens, the engine compresses conversation history via a dedicated API call.
      • Session memory: File-backed persistent memory (MEMORY.md) surviving across sessions.
      • Durable memory extraction: Optional LLM-powered pass that extracts key learnings from conversation turns into persistent memory.
      • Python GC: Standard CPython reference counting + cycle collector. No custom memory pools.

      Concurrency Concerns #

      1. GIL limitation: Python's GIL means CPU-bound tool operations (e.g., large text processing in a tool's execute()) block the event loop. Mitigated by delegating heavy work to subprocesses (Bash tool, subprocess agents).
      2. Mutable metadata dict: ToolExecutionContext.metadata is a plain dict shared across hook execution and tool execution within a single invocation. No locking — safe only because the asyncio model is single-threaded.
      3. Subprocess agent lifecycle: Coordinator spawns subagent processes. If the parent dies, orphan subagent processes may persist. No explicit cleanup daemon.
      4. §10 Performance Characteristics #

        Published Benchmarks #

        No formal benchmarks published. The test suite validates correctness, not performance.

        Startup #

        MetricEstimate
        Cold start (oh)~1-3 s (Python startup + typer + module import + config load + provider discovery)
        Warm start (--continue)+session replay time (proportional to conversation JSONL size)

        Scaling Behavior #

        • Context length: Linear cost/latency degradation. Auto-compact mitigates but summaries are lossy.
        • Tool count: 43 built-in + MCP-discovered tools included as function definitions in every API call. ToolSearch tool defers loading of rarely-used tools to manage context budget.
        • Subagent depth: Each subagent spawns a new process with a fresh API conversation. Deep nesting multiplies API calls. max_turns per agent bounds each level.
        • Provider latency variance: Performance depends heavily on the backend — local Ollama adds minimal network latency but slow inference; cloud providers add network RTT but fast inference. The harness is transparent to this.

        §11 论証鏈 #

        StepClaimEvidenceDepends on
        1A production coding agent needs more than a bare tool-use loop — it needs permissions, memory, extensibility, and multi-provider supportClaude Code's closed-source TypeScript harness proves the concept but locks users to one provider and one language
        2Provider abstraction via SupportsStreamingMessages protocol decouples the agent loop from any specific LLM backendsrc/openharness/api/ contains separate Anthropic-compat, OpenAI-compat, and Copilot clients, all consumed by the same QueryEngineStep 1
        3Pydantic-based BaseTool with ToolRegistry makes the 43-tool inventory maintainable and extensibleEach tool is ~30-50 lines of meaningful logic (input model + execute); schema generation is automatic via model_json_schema()Step 1
        4Multi-level permission system (default/auto/plan) + hook system provides safety without sacrificing power-user velocityDefault mode asks; auto mode in sandboxes skips prompts; plan mode blocks writes entirely. Hooks intercept at the tool-call boundary.Steps 1, 3
        5Persistent memory (MEMORY.md + session memory + durable extraction + auto-dream) enables long-horizon agent effectivenessMemory pipeline runs post-turn: session memory persists across conversation, durable extraction uses LLM to identify key learnings, auto-dream schedules background consolidationSteps 1-4
        6The ohmo personal agent layer proves the harness is general enough to support a fundamentally different product surface (persistent multi-channel agent) beyond CLI sessionsohmo uses the same QueryEngine and tool system but adds gateway architecture, soul/identity, and Telegram/Slack/Discord/Feishu channelsSteps 1-5

        §12 Tech Debt & Code Quality #

        Test Coverage #

        SuiteCountType
        Unit + Integration114Pytest
        CLI Flags E2E6Real model calls
        Harness Features E2E9Retry, skills, parallel, permissions
        React TUI E2E3Welcome, conversation, status
        TUI Interactions E2E4Commands, permissions, shortcuts
        Skill/Plugin Compat12anthropics/skills + claude-code/plugins

        Known Debts #

        IssueSeverityEvidence
        cli.py at 91KBHighSingle file containing the entire typer CLI app — likely 2000+ lines. Refactoring risk for any CLI flag change.
        ~3M chars of PythonMediumDescribed as "lightweight" in the README, but the codebase is a full-featured production system. Newcomer onboarding friction.
        Rapid version churn (9 releases in ~5 weeks)MediumAPI surface may not be stable. Breaking changes between minor versions likely.
        Claude Code compatibility surfaceMediumDeliberate compatibility with anthropics/skills and claude-code/plugins formats creates coupling to Anthropic's undocumented conventions. If Anthropic changes these formats, OpenHarness must follow.
        Dual product in one repoLow-Mediumohmo (personal agent) and OpenHarness (harness library) share a repo. Separate concerns but coupled releases.

        Dependency Health #

        DependencyRoleRisk
        anthropic>=0.40.0Anthropic API clientActively maintained, primary provider
        openai>=1.0.0OpenAI-compat clientActively maintained
        pydantic>=2.0.0Tool input validationStable, widely adopted
        rich>=13.0.0Terminal formattingActively maintained
        typer>=0.12.0CLI frameworkActively maintained
        mcp>=1.0.0MCP protocol clientEarly-stage, spec still evolving
        python-telegram-bot>=21.0.0Telegram channelActively maintained
        slack-sdk>=3.0.0Slack channelActively maintained
        discord.py>=2.0.0Discord channelMaintenance varies (community-driven)
        lark-oapi>=1.5.0Feishu channelRegional dependency (ByteDance)

        CI #

        GitHub Actions CI workflow added in v0.1.8. Details of the CI matrix not specified in the L1.

        §13 Community Health #

        MetricValue
        Stars13,112 (in ~8 weeks)
        LicenseMIT
        Maintainer orgHKUDS (HKU Data Science Lab)
        Key contributorsnovix-science, HKUDS team
        Growth rate~1,600 stars/week (viral)
        Release cadence9 releases in 5 weeks (2026-04 → 2026-05-07)
        GovernanceUniversity lab project, open to community contributions
        CONTRIBUTING.mdPresent

        The 13K stars in 8 weeks signals strong community demand for an open-source Claude Code alternative. The university-lab origin (HKUDS) provides research credibility but raises questions about long-term maintenance if the lab's focus shifts. The MIT license maximizes adoption potential.

        §14 Comparison with Alternatives #

        DimensionOpenHarnessClaude CodeAiderContinue.dev
        LanguagePythonTypeScriptPythonTypeScript
        LicenseMIT (open)ProprietaryApache-2.0Apache-2.0
        Provider support10+ backends (Anthropic, OpenAI, Copilot, Gemini, DeepSeek, Ollama, etc.)Anthropic onlyMulti-model (GPT, Claude, etc.)Multi-model
        Tool count43 built-in + MCP + plugins40+ built-in + MCP + pluginsShell + file editIDE-integrated
        Permission system3-mode (default/auto/plan) + hooks4-stage pipeline + hooksYes/no per editIDE-native
        Multi-agentSubagent spawning + swarm coordination5 spawn modes + team protocols + coordinatorNoneNone
        Context managementAuto-compact + session memory3 strategies (auto/snip/collapse)Repo map + chat historyIDE context
        MCP supportFull (HTTP transport, auto-reconnect)Full (stdio/sse/http/ws/sdk, OAuth)NoneMCP client
        Memory persistenceMEMORY.md + durable extraction + auto-dreamCLAUDE.md + session JSONLGit-basedIDE history
        Personal agentohmo (Telegram/Slack/Discord/Feishu)NoneNoneNone
        Skill/plugin compatanthropics/skills + claude-code/pluginsNativeLimitedExtensions
        Docker sandboxYesNo (native sandbox)NoNo
        Stars13KN/A (proprietary)~30K~20K
        Codebase size~3M chars Python~512K LOC TypeScript~30K LOC Python~50K LOC TypeScript

        Winner by scenario:

        • Open-source Claude Code alternative with multi-provider flexibility → OpenHarness (MIT, 10+ backends, Claude Code plugin/skill compat)
        • Maximum harness sophistication and production maturity → Claude Code (4-stage permissions, 5 spawn modes, 3 compression strategies)
        • Lightweight model-agnostic coding assistant → Aider (30K LOC, minimal dependencies, Git-native)
        • Persistent personal agent across chat platforms → OpenHarness/ohmo (only entrant with this capability)
        • IDE-integrated experience → Continue.dev

        §15 実現 Cross-Reference #

        Key file citations #

        ComponentFileRole
        Tool contractsrc/openharness/tools/base.pyBaseTool, ToolRegistry, ToolResult, ToolExecutionContext
        Agent loopsrc/openharness/engine/query_engine.pyQueryEngine.submit_message()AsyncIterator[StreamEvent]
        CLI entrysrc/openharness/cli.py (91KB)typer app: flags, subcommands, session management
        API clientssrc/openharness/api/Anthropic-compat, OpenAI-compat, Copilot clients
        Permissionssrc/openharness/permissions/3-mode permission system
        Hookssrc/openharness/hooks/PreToolUse / PostToolUse lifecycle
        Memorysrc/openharness/memory/MEMORY.md, session memory, durable extraction
        Skillssrc/openharness/skills/On-demand .md loading from 4 locations
        Pluginssrc/openharness/plugins/commands + hooks + agents + MCP plugins
        Commandssrc/openharness/commands/54 slash commands
        MCP clientsrc/openharness/mcp/HTTP transport, auto-reconnect
        Coordinatorsrc/openharness/coordinator/Subagent spawning, team registry
        Swarmsrc/openharness/swarm/Multi-agent swarm coordination
        Sandboxsrc/openharness/sandbox/Native + Docker backend
        Configsrc/openharness/config/Multi-layer config, provider profiles, migrations
        Promptssrc/openharness/prompts/System prompt assembly, CLAUDE.md injection
        Servicessrc/openharness/services/autodream, session memory, memory extraction
        ohmo CLIohmo/cli.pyPersonal agent entry point
        Channelssrc/openharness/channels/Telegram, Slack, Discord, Feishu
        TUI frontendfrontend/terminal/React/Ink TypeScript TUI
        Package configpyproject.tomlhatchling build, entry points, dependencies

        関鍵実装細節 #

        1. Pydantic input_model as the tool contract boundary: Each tool declares a BaseModel subclass as its input_model. The to_api_schema() method calls model_json_schema() to generate the JSON Schema sent to the LLM. This means the same Pydantic model serves three purposes: (a) documentation for the LLM (via JSON Schema description fields), (b) input validation at runtime (Pydantic parsing rejects malformed arguments), and (c) type-safe access in execute() (the arguments parameter is the parsed model instance). This triple-duty design eliminates schema-code drift — if a tool's input changes, the schema, validation, and code all update from a single source of truth.
          1. ToolResult as frozen dataclass, not dict: By making ToolResult a frozen dataclass rather than a plain dict, the codebase prevents a subtle mutation bug: if a tool's output were stored in a mutable dict, downstream code (e.g., conversation history serialization, memory extraction) could accidentally mutate it, causing non-reproducible conversation state. The frozen=True constraint eliminates this class of bugs at the Python level.
          2. §16 Verdict & Recommendations #

            When to adopt ("yes" regime) #

            • You want an open-source (MIT), Python-native agent harness that works with your existing LLM provider — OpenHarness supports 10+ backends out of the box, including local Ollama.
            • You need Claude Code's tool/skill/plugin ecosystem but cannot use the proprietary binary — OpenHarness maintains deliberate compatibility with anthropics/skills and claude-code/plugins formats.
            • You want a persistent personal agent accessible via Telegram, Slack, Discord, or Feishu — ohmo is the only open-source entrant with multi-channel chat integration.
            • You are a researcher studying agent harness architecture and want modifiable Python source rather than decompiled TypeScript.
            • You want to extend the tool set with custom Python tools — the BaseTool + Pydantic pattern is straightforward and well-documented.

            When NOT to adopt ("no" regime) #

            • You need maximum harness sophistication — Claude Code's 4-stage permission pipeline, 5 spawn modes, 3 compression strategies, and 108 feature-gated modules exceed OpenHarness's current capabilities.
            • You need production stability guarantees — 9 releases in 5 weeks (v0.1.x) signals rapid iteration, not API stability. Breaking changes between versions are likely.
            • You want a battle-tested system — 114 tests is thin coverage for a 3M-char codebase. Edge cases in multi-provider streaming, concurrent tool dispatch, and subprocess agent lifecycle are likely under-tested.
            • You need IDE integration — OpenHarness is CLI-first. Use Cursor or Continue.dev for IDE-embedded agent experiences.
            • The 91KB cli.py monolith is a maintenance risk — if you plan to fork and heavily customize the CLI surface, expect refactoring work.

            Suggested contributions if you adopted #

            1. Split cli.py: Factor the 91KB monolith into separate modules per concern (session management, provider config, output formatting, flag parsing).
            2. Add structured benchmarks: Publish latency and token-efficiency benchmarks across providers (Anthropic vs. OpenAI vs. Ollama) to validate the provider abstraction's overhead.
            3. Fuzz the multi-provider streaming path: The normalization of incompatible SSE formats (Anthropic content_block_delta vs. OpenAI choices[0].delta) is the highest-risk code path — fuzz it with malformed/partial SSE events.
            4. Decouple ohmo into a separate package: The personal agent has a distinct product surface, dependency set (Telegram, Slack, Discord, Feishu SDKs), and release cadence from the core harness. Separate packages would reduce install footprint for harness-only users.