Skip to main content

A small pluggable AI agent harness with sessions and tools.

Project description

My Harness

My Harness is a small educational AI agent harness.

It has:

  • terminal CLI command: my-harness
  • pluggable LLM providers
  • OpenAI, Google (Gemini), and OpenRouter providers included
  • session creation/resume/listing
  • session storage as JSON
  • tool-calling loop
  • scalable tools folder structure
  • prompt folder for agent instructions
  • skills folder for optional agent behaviors
  • subagent creation tool
  • workspace-restricted file tools
  • bash disabled by default
  • tools: read_file, write_file, optional bash

Install for local development

cd "C:/Users/shiva/Downloads/custom harness"
python -m venv .venv
.venv/Scripts/activate
python -m pip install --upgrade pip
pip install -e .

Now run from anywhere:

my-harness

or:

myharness

A command with a space like my harness is not recommended because terminals treat spaces as separators. Use my-harness.

Configure

After installing, users can create API key config with:

my-harness --workspace "C:/path/to/project" --init-config

This creates:

<workspace>/.env

Then edit that file and add the API key.

Alternatively, manually create .env in the folder where you run my-harness or inside the selected workspace:

LLM_PROVIDER=openai
OPENAI_API_KEY=your_api_key_here
OPENAI_MODEL=gpt-4o-mini

Or set the API key globally in your shell.

Windows PowerShell:

setx OPENAI_API_KEY "your_api_key_here"

macOS/Linux:

export OPENAI_API_KEY="your_api_key_here"

You can copy the example:

copy .env.example .env

Usage

Create a new session in the current folder as workspace:

my-harness

Use a specific workspace:

my-harness --workspace "C:/path/to/project"

List sessions in current folder:

my-harness --list-sessions

Resume a session:

my-harness --session <session-id>

Choose provider/model:

my-harness --provider openai --model gpt-4o-mini

Interactive model/API-key setup:

my-harness --setup-model

Inside chat, use:

/model

Currently supported providers: openai, google (or gemini), openrouter.

Provider Configuration & Models:

  1. OpenAI (LLM_PROVIDER=openai)

    • Env Vars: OPENAI_API_KEY, OPENAI_MODEL
    • Models: gpt-4o-mini, gpt-4o, gpt-4.1-mini, gpt-4.1, o4-mini
  2. Google Gemini (LLM_PROVIDER=google)

    • Env Vars: GEMINI_API_KEY (or GOOGLE_API_KEY), GEMINI_MODEL
    • Models: gemini-2.5-flash, gemini-2.5-pro, gemini-2.0-flash, gemini-1.5-flash, gemini-1.5-pro
  3. OpenRouter (LLM_PROVIDER=openrouter)

    • Env Vars: OPENROUTER_API_KEY, OPENROUTER_MODEL
    • Models: anthropic/claude-3.5-sonnet, openai/gpt-4o-mini, google/gemini-2.5-flash, deepseek/deepseek-chat, meta-llama/llama-3.3-70b-instruct

If API keys are missing, chat will not start. The setup flow asks the user to paste the key first.

Disable subagents if you do not want the agent to spawn focused helper agents:

my-harness --no-subagents

Enable bash if you need shell commands:

my-harness --enable-bash

Important: read_file and write_file are restricted to the workspace. bash is not a strong workspace sandbox because shell commands can still try to access paths outside the workspace if your OS allows it. For strong isolation, run my-harness inside Docker/VM.

Use custom prompt files:

my-harness --system-prompt ./system.md --user-prompt ./prompt.md

Or set them in .env:

MY_HARNESS_SYSTEM_PROMPT=C:/path/to/system.md
MY_HARNESS_USER_PROMPT=C:/path/to/prompt.md

Meaning:

  • system.md = common agent instructions for all agents
  • prompt.md = specialized user/project instructions

Sessions

Sessions are stored inside the selected workspace:

<workspace>/.my_harness/sessions/<session-id>.json

Each session contains:

{
  "id": "uuid",
  "created_at": "timestamp",
  "updated_at": "timestamp",
  "messages": []
}

Project structure

custom harness/
  pyproject.toml
  MANIFEST.in
  README.md
  .env.example
  my_harness/
    __init__.py
    main.py
    agent.py
    session.py
    llm.py
    prompts/
      __init__.py
      loader.py
      system.md
      prompt.md
    skills/
      __init__.py
      loader.py
      subagents.md
    providers/
      __init__.py
      base.py
      factory.py
      openai_provider.py
      google_provider.py
      openrouter_provider.py
    tools/
      __init__.py
      base.py
      runner.py
      filesystem/
        read_file.py
        write_file.py
      shell/
        bash.py
      subagents/
        create_subagent.py
  docs/
    packaging.md

Add another tool

Create a new file under my_harness/tools/, for example:

my_harness/tools/network/http_get.py

Implement BaseTool:

from ..base import BaseTool

class HttpGetTool(BaseTool):
    name = "http_get"
    description = "Fetch a URL."

    def schema(self):
        return {
            "type": "function",
            "function": {
                "name": self.name,
                "description": self.description,
                "parameters": {
                    "type": "object",
                    "properties": {"url": {"type": "string"}},
                    "required": ["url"],
                },
            },
        }

    def execute(self, url: str) -> str:
        return "TODO: fetch URL"

Then register it in my_harness/tools/runner.py.

Skills and subagents

Skill instructions live in:

my_harness/skills/

The included skill file is:

my_harness/skills/subagents.md

It teaches the main agent when to use the create_subagent tool.

The subagent tool:

  • creates a separate session
  • gives the subagent a focused task
  • lets it use workspace-safe tools
  • returns the result to the main agent

Subagent sessions are stored in the same workspace session folder and marked with:

"kind": "subagent"

Agent prompts

Default instructions live in:

my_harness/prompts/system.md

You can edit that file during development, or pass a custom prompt at runtime:

my-harness --system-prompt ./system.md

Add another LLM provider

Create:

my_harness/providers/my_provider.py

Example:

from .base import LLMProvider

class MyProvider(LLMProvider):
    def __init__(self, model=None):
        self.model = model or "my-default-model"

    def chat(self, messages, tools):
        # Call your provider API here.
        # Return a response compatible with agent.py.
        pass

Register it in my_harness/providers/factory.py:

from .my_provider import MyProvider

SUPPORTED_PROVIDERS = {
    "openai": OpenAIProvider,
    "myprovider": MyProvider,
}

Then run:

my-harness --provider myprovider

Build package

pip install build twine
python -m build

Output appears in:

dist/

Install the wheel on another system:

pip install dist/my_harness-0.1.0-py3-none-any.whl

More docs

See:

docs/packaging.md

Workspace access and safety

File access is controlled in my_harness/tools/base.py using ToolContext.safe_path().

This blocks paths like:

../secret.txt
C:/Users/shiva/secret.txt

when they are outside the configured workspace.

By default, only workspace-safe file tools are enabled:

  • read_file
  • write_file

The bash tool is disabled by default. Enable it only when needed:

my-harness --enable-bash

For true security, use Docker/VM/container isolation.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

my_harness-0.1.1.tar.gz (19.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

my_harness-0.1.1-py3-none-any.whl (22.2 kB view details)

Uploaded Python 3

File details

Details for the file my_harness-0.1.1.tar.gz.

File metadata

  • Download URL: my_harness-0.1.1.tar.gz
  • Upload date:
  • Size: 19.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.0

File hashes

Hashes for my_harness-0.1.1.tar.gz
Algorithm Hash digest
SHA256 bbc26f0ac767871b5d5fa2c260be12a25e24c21be8d13bf7db4d4a93b0002ec0
MD5 bc9096a8568b47c89455d58abf52ee4a
BLAKE2b-256 35e0c2eeea6c85150c2484a78d420b05c3b759d1df37e6cda9cd227cb8895b79

See more details on using hashes here.

File details

Details for the file my_harness-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: my_harness-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 22.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.0

File hashes

Hashes for my_harness-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 884feaffac5c29696ee07e0fb32284e923e69240bc2a6a3e8b97c829055cf7bc
MD5 937fa36a476af05360f0280b1b7f7d04
BLAKE2b-256 1c49f883eb1765e287b67e5fd9e890700ce267449bf920019c0e31bea9fcf8a8

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page