A minimal Python SDK for agent loops, with streaming events, clean tool contracts, and zero ceremony.
Project description
EasyHarness
A minimal Python SDK for agent loops, with streaming events, clean tool contracts, and zero ceremony.
Small public surface. Strong defaults. Explicit control.
Why EasyHarness
EasyHarness is designed for local agent workflows where a tiny SDK surface matters more than a large framework.
It works well for coding agents, but it is not limited to them. Any agent that benefits from strict tool contracts, streaming events, and explicit runtime control can use the same SDK surface.
It gives you:
- A single primary runtime entry point through
Agent - Strict tool metadata and output contracts through
toolandToolOutput - A unified streaming event model for thinking, tool, assistant, compression, and system events
- An official FileGlide-backed filesystem toolset that can be auto-loaded by default or scoped explicitly
Public Surface
The root package intentionally exposes only five public names:
AgentModelConfigAgentEventToolOutputtool
Everything else stays behind internal modules or the explicit
easyharness.toolset package.
Installation
EasyHarness supports both pip and uv.
Install with pip
pip install -U easyharness
Install with uv
uv add -U easyharness
Verify the installation
After installation, import the public package surface in Python:
from easyharness import Agent, ModelConfig, AgentEvent, ToolOutput, tool
If the import succeeds, the SDK is ready to use.
Typical Usage
1. Define a tool and run an agent
from easyharness import Agent, ModelConfig, ToolOutput, tool
@tool(
name="ping_tool",
purpose="Return a fixed response for a minimal tool-flow check.",
when_to_use=(
"Use this when the model needs a trivial tool call to verify that the "
"tool pipeline is available."
),
parameters={},
returns="A fixed pong response.",
common_failures=["This tool does not fail under normal conditions."],
)
def ping_tool() -> ToolOutput:
return ToolOutput(
data={"value": "pong"},
model_text="pong",
preview="pong",
detail='{"value": "pong"}',
)
agent = Agent(
model=ModelConfig(
model="openai/gpt-4.1-mini",
api_key="YOUR_API_KEY",
),
system_prompt="You are a precise agent.",
tools=[ping_tool],
)
print(agent.run("Call the ping tool and confirm that the tool pipeline works."))
2. Use the default FileGlide integration
Agent can auto-load the official FileGlide-backed toolset, so the shortest
agent setup does not need a manual tools=[...] list.
from easyharness import Agent, ModelConfig
agent = Agent(
model=ModelConfig(
model="openai/gpt-4.1-mini",
api_key="YOUR_API_KEY",
),
system_prompt="You are a careful agent.",
)
print(
agent.run(
"List the workspace, read pyproject.toml, and summarize the SDK shape."
)
)
Official default FileGlide tools
fileglide_list_treefileglide_search_pathsfileglide_read_textfileglide_search_textfileglide_edit_textfileglide_manage_pathsfileglide_inspect_path
3. Disable the default FileGlide tools
If you want a stricter default runtime with no filesystem tool auto-loading, disable it explicitly.
from easyharness import Agent, ModelConfig
agent = Agent(
model=ModelConfig(
model="openai/gpt-4.1-mini",
api_key="YOUR_API_KEY",
),
system_prompt="You are a careful agent.",
enable_fileglide=False,
)
4. Build a scoped official FileGlide toolset
When you need explicit control over the filesystem scope, disable the default
auto-load and pass a scoped official toolset from easyharness.toolset.
from easyharness import Agent, ModelConfig
from easyharness.toolset import build_fileglide_tools
agent = Agent(
model=ModelConfig(
model="openai/gpt-4.1-mini",
api_key="YOUR_API_KEY",
),
system_prompt="You are a careful agent.",
enable_fileglide=False,
tools=build_fileglide_tools(default_root="D:/Projects/PythonProjects/EasyHarness"),
)
build_fileglide_tools(default_root=...) creates the official scoped toolset. Paths
that escape the configured root are rejected by FileGlide scope protection.
Each official fileglide_* tool also accepts an optional root argument for
that single call. When provided, it overrides the builder's default root
without additional SDK-level path-range restrictions.
tools = build_fileglide_tools(default_root="D:/Projects/PythonProjects/EasyHarness")
tool_map = {tool.tool_name: tool for tool in tools}
result = tool_map["fileglide_read_text"](
target="EasyHarness/pyproject.toml",
root="D:/Projects/PythonProjects",
)
Conversation Compression
EasyHarness uses an eventing summarizing conversation manager by default. That default policy is owned by the SDK rather than inherited implicitly from the current Strands version.
The default compression behavior is:
- proactive compression enabled by default
- proactive compression threshold at
70%of the model context window summary_ratio=0.3preserve_recent_messages=8
This means agent.stream(...) may emit compress events before a model call
actually overflows its context window.
If you need a different policy, pass a custom conversation_manager. If you
want to keep the built-in eventing manager but change its defaults, create it
explicitly and pass it to Agent:
from easyharness import Agent, ModelConfig
from easyharness._internal.conversation import (
EventingSummarizingConversationManager,
)
agent = Agent(
model=ModelConfig(
model="openai/gpt-4.1-mini",
api_key="YOUR_API_KEY",
),
system_prompt="You are a careful agent.",
conversation_manager=EventingSummarizingConversationManager(
proactive_compression=None,
preserve_recent_messages=12,
),
)
Event Stream
agent.stream(prompt) yields a unified AgentEvent stream. The public event
kinds are:
thinkingtoolassistantcompresssystem
Each event uses the same status vocabulary:
starteddeltacompletedfailed
for event in agent.stream("Inspect the workspace and explain the next step."):
print(event.kind, event.status, event.text)
compress events cover both proactive and reactive compression attempts. The
event data payload includes the compression mode so UIs can distinguish
between "compressed early to stay under the limit" and "compressed during
overflow recovery".
Design Boundaries
EasyHarness is intentionally small. v1 does not try to solve:
- UI components
- Environment-variable orchestration layers
- Plugin platforms
- Multi-agent orchestration
- Root-package re-export sprawl for toolset builders
Summary
EasyHarness is most useful when you want:
- A minimal
AgentAPI - Strict tool definitions
- Streaming runtime visibility
- A practical, official FileGlide-backed filesystem toolset
If that is your shape of problem, the shortest path is:
- Create an
Agent - Define tools with
@tool(...) - Use the default FileGlide integration or pass a scoped official toolset
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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file easyharness-0.1.1.tar.gz.
File metadata
- Download URL: easyharness-0.1.1.tar.gz
- Upload date:
- Size: 32.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.2 {"installer":{"name":"uv","version":"0.11.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2862c63c1f7a25ce19c28d49f8964476aa2efb697eefef474a4e9758713b5219
|
|
| MD5 |
9809ea3ffcecb2cf0c4b9f7e15c26598
|
|
| BLAKE2b-256 |
d7c20be4ebca8353d6b75146e8e5f5640cea2348f9e4b19da5b05ed667b8e6ab
|
File details
Details for the file easyharness-0.1.1-py3-none-any.whl.
File metadata
- Download URL: easyharness-0.1.1-py3-none-any.whl
- Upload date:
- Size: 25.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.2 {"installer":{"name":"uv","version":"0.11.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ec8b582d5c956416cab5a153d4c5ee13c08a96baae6152548bd31e516b4218e2
|
|
| MD5 |
bc579fe57bbe281a94356eeaf23e40fc
|
|
| BLAKE2b-256 |
59787a011d54ba4eaefeb83b7d535896a5c4bde86e77c6d9e0808812863e4820
|