OpenHands Software Agent SDK

repository·main·Indexed 21 days ago

https://github.com/openhands/software-agent-sdk

A framework of Python and REST APIs for building autonomous agents that interact with codebases. It supports local and remote execution via an Agent Server in Docker or Kubernetes, featuring a hooks system for intercepting agent behavior, support for custom tool implementation with dynamic registration, and integration templates for GitHub Actions to automate routine maintenance and PR reviews.

Tokens
29.6K
Snippets
82
Records
133
Agent score
75%

What's inside openhands-software-agent-sdk

  1. Overview of OpenHands Agent Server

    main

    The OpenHands Agent Server is a minimal REST API and WebSocket server designed to provide a programmatic interface for interacting with OpenHands AI agents. It is optimized for development, testing, and lightweight deployments by using the local filesystem for storing conversations, events, and workspace files.

    Key features include:

    • REST API: Full CRUD operations for conversations and events.
    • WebSocket Support: Real-time communication with agents.
    • Local Storage: File-based storage for conversations and workspace data.
    • CORS Support: Configurable cross-origin resource sharing.
    • Authentication: Optional session-based API key authentication.
    • Webhooks: Configurable webhook notifications for events.
    • Auto-reload: Development mode with automatic code reloading.
  2. Use the OpenAI-compatible gateway

    main

    The openhands-agent-server package provides an OpenAI-compatible API gateway accessible under the /v1 endpoint. This allows you to interact with OpenHands agents using standard OpenAI client libraries by translating OpenAI chat completion requests into OpenHands conversations.

    Key components of the gateway:

    • Routing: Maps OpenAI-style bearer authentication to the OpenHands session key mechanism.
    • Translation: The service layer translates OpenAI chat completion requests into OpenHands conversations, manages the execution lifecycle, and returns responses in the expected OpenAI format.
  3. Use the Terminal Tool for persistent shell sessions

    main

    The TerminalTool provides a persistent shell session (bash-compatible) for executing commands within the OpenHands SDK. Unlike single-command execution, this tool maintains state such as environment variables, virtual environments, and the current working directory between consecutive commands.

    Key Features

    • Persistent session: State is preserved across commands.
    • Backend support: Uses tmux if available, otherwise falls back to a subprocess-based PTY.
    • Configurable environment: Allows setting session-scoped environment variables that are not exposed to the LLM.
    • Long-running commands: Supports soft timeouts and interrupts.
    • Terminal reset: Ability to reset the session if it becomes unresponsive.
    from openhands.sdk import Conversation
    from openhands.tools.terminal.definition import TerminalTool, TerminalAction
    
    conversation = Conversation()
    tools = TerminalTool.create(conv_state=conversation.state)
    terminal = tools[0]
    
    # Execute a command
    action = TerminalAction(command="echo 'Hello, World!'")
    result = terminal.executor(action)
    print(result.text)
  4. PostHog Error Debugging Workflow Overview

    main

    The PostHog Error Debugging Workflow is an automated system designed to reduce Mean Time To Recovery (MTTR) by using AI to analyze code and identify root causes of errors reported in PostHog.

    Key capabilities include:

    • Automated Debugging: AI-driven code analysis without manual intervention.
    • Smart Issue Management: Prevents duplicate issues from cluttering tracking systems.
    • Multi-Repository Analysis: Provides context-aware debugging by analyzing multiple repositories to form a complete picture of an error.
    • AI-Powered Insights: Generates actionable recommendations for fixes.
    • Scalability: Easily extensible to new event categories.
    • User-Centricity: Tracks errors based on actual user experiences.
  5. How the extension installation framework works

    main

    The installation module is a generic, extension-type agnostic framework for installing, tracking, and loading extensions from local or remote sources.

    It relies on two main components:

    1. An extension type T: Any object that possesses name, version, and description attributes (typically a Pydantic BaseModel).
    2. An InstallationInterface[T]: A loader implementation that defines how to load the extension type T from a specific directory.

    The framework handles all other logic generically, including fetching, copying, metadata bookkeeping, and managing the enabled/disabled state.

    from pathlib import Path
    from pydantic import BaseModel
    from openhands.sdk.extensions.installation import (
        InstallationInterface,
        InstallationManager,
    )
    
    class Widget(BaseModel):
        name: str
        version: str
        description: str
    
    class WidgetLoader(InstallationInterface[Widget]):
        @staticmethod
        def load_from_dir(extension_dir: Path) -> Widget:
            return Widget.model_validate_json(
                (extension_dir / "widget.json").read_text()
            )
  6. How to use the web-researcher subagent

    main

    The web-researcher is a specialized subagent designed for researching documentation, API references, changelogs, and other publicly available web content. It returns a structured summary of findings accompanied by source URLs.

    Available Interfaces

    1. Tavily search (tavily_search): Use this as your first choice for fast, API-based web searches.
    2. Fetch (fetch): A lightweight URL fetcher for grabbing text content from a specific URL without a full browser. Note: fetch respects robots.txt and may be blocked from sites that a browser can access.
    3. Browser tools: A full browser for navigating, reading, and interacting with web UIs when simpler tools are insufficient.
    name: web-researcher
    model: inherit
    description: >-
        USE THIS when you need to research information on the web — documentation,
        API references, changelogs, Stack Overflow answers, or any publicly available
        content. Returns a structured summary of findings with source URLs.
    tools:
      - browser_tool_set
    mcp_servers:
      fetch:
        command: uvx
        args: ["--with", "mcp==1.29.0", "mcp-server-fetch==2026.7.10"]
      tavily:
        command: npx
        args: ["-y", "tavily-mcp@0.2.1"]
        env:
          TAVILY_API_KEY: "${TAVILY_API_KEY}"
  7. Reporting requirements for the bash-runner

    main

    When using the bash-runner, the agent is expected to follow strict reporting protocols to avoid dumping raw terminal output. The reporting format depends on the task type:

    • Test Suites: Report total passed, failed, skipped, and errored counts. For failures, include the test name, a short reason (assertion message or exception), and the file:line location. Do not include passing test names or full tracebacks.
    • Builds and Linters: Report success or failure. For errors/warnings, include file:line, the message, and a one-line summary. Do not include progress lines (e.g., "compiling X...").
    • Git Operations: Report the branch, commit hash, files affected, and any conflicts or errors.
    • Other Commands: Report the exit code (if non-zero) and key output lines that answer the specific request.
  8. Use ApptainerWorkspace for containerized environments

    main

    The ApptainerWorkspace provides a container-based workspace using Apptainer (formerly Singularity). It is designed for High-Performance Computing (HPC) and shared environments where Docker is unavailable or root access is restricted.

    Important Limitation: This class only works with pre-built images. It does not support building images on-the-fly from a base image. For on-the-fly building with Docker, use DockerDevWorkspace instead.

    from openhands.workspace import ApptainerWorkspace
    
    with ApptainerWorkspace(server_image="ghcr.io/openhands/agent-server:latest-python") as workspace:
        result = workspace.execute_command("echo 'Hello!'")
        print(result.stdout)
  9. Control hook execution using exit codes

    main

    Hook scripts signal their result to the SDK using exit codes. This follows the Claude Code hook contract:

    • 0 (Success): The operation proceeds. If the hook writes to stdout, the SDK parses it as JSON for structured output containing decision, reason, and additionalContext.
    • 2 (Block): The operation is denied. For Stop hooks, this prevents the agent from finishing and forces it to continue running. For other hooks, it blocks the specific action. stderr or the reason field is surfaced as feedback to the agent.
    • Any other non-zero exit code (e.g., 1): Treated as a non-blocking error. The error is logged, but the operation proceeds.

    Important: To enforce a policy (e.g., blocking a command), you must use exit code 2.

  10. How Context, AgentContext, and Skills work together

    main

    Context is the mechanism used to provide agents with skills and knowledge during a conversation. It is composed of two main parts:

    1. AgentContext: The container that composes various skills and runtime context. You pass an AgentContext instance to an Agent to condition its behavior.
    2. Skill: A unit of structured knowledge. Each skill is defined by a name, content, and a source, and is activated based on a trigger type:
      • trigger=None: The skill is always active and provides repository-wide context for all conversations.
      • KeywordTrigger: The skill activates only when specific keywords are detected in user messages.
      • TaskTrigger: The skill activates based on specific task-related conditions.

    Additionally, AgentContext supports time awareness via current_datetime, which defaults to datetime.now().

    from openhands.sdk.context import AgentContext, KeywordTrigger, Skill
    
    agent_context = AgentContext(
        skills=[
            Skill(
                name="repo-guidelines",
                content="Repository-wide coding standards and best practices.",
                source="AGENTS.md",
                trigger=None,  # Always-active skill
            ),
            Skill(
                name="flarglebargle",
                content="If the user says flarglebargle, compliment them.",
                source="flarglebargle.md",
                trigger=KeywordTrigger(keywords=["flarglebargle"]),
            ),
        ],
    )
  11. Understand OpenHands Telemetry and Consent

    main

    The agent server can emit product-analytics events (e.g., agent_server.conversation_started) to PostHog or an HTTP endpoint. This is disabled by default.

    Consent is managed via misc_settings.telemetry.consent (granted | denied | unset).

    • Precedence: DO_NOT_TRACK=1 > OH_TELEMETRY_CONSENT (if mode is override) > misc_settings.telemetry.consent > OH_TELEMETRY_CONSENT (if mode is seed).
    • Revocation: When consent is revoked, delivery stops immediately and queued events are discarded.

    Exporters

    • none: Default. No delivery.
    • posthog: Requires OH_TELEMETRY_POSTHOG_API_KEY and the [posthog] pip extra.
    • http: POSTs sanitized batches to OH_TELEMETRY_HTTP_ENDPOINT.

    Privacy

    Telemetry only sends allowlisted lifecycle/failure events. It never sends prompts, messages, file contents, secrets, or tracebacks. Magnitudes (like token counts or costs) are bucketed to prevent re-identification.

  12. Use agent-based hooks for semantic reasoning

    main

    An agent-based hook (type="agent") is a lifecycle hook where the decision (allow/deny) is produced by an LLM-driven sub-agent rather than a static shell script.

    Unlike shell-based PreToolUse hooks that rely on literal string matching (which can be bypassed by command obfuscation), agent hooks reason about the semantic intent of an action. For example, an agent hook can identify that a command's intent is to "read a sensitive system file" and deny it, even if the command doesn't match a specific keyword blacklist.

    Common use cases include:

    • Security Reviewer (PreToolUse): Denying commands based on intent (e.g., accessing /etc/passwd).
    • Quality Reviewer (Stop): Refusing to let the main agent finish until specific deliverables (e.g., REPORT.md) are present in the workspace.