Skip to main content

agents-harness

Version 0.1.1 MCP Python 3.10+ PyPI License

Declarative scheduled flow runner, per-request agent loop, and job kernel for local agent services.
Zero heavy dependencies (Python standard library only). No Celery, no Redis, no cron daemons.


Quickstart

pip install agents-harness

Optional FastMCP server support:

pip install "agents-harness[mcp]"

Optional trace sealing and audit (--seal, mcp.traces.seal, mcp.traces.audit) need agents-traces 0.1.0 or newer:

pip install "agents-harness[traces]"

Calendar tools (mcp.calendar.add, mcp.calendar.list) forward --calendar, which needs agents-calendar 0.1.0 or newer. dtstart, dtend, and list ranges are ISO-8601 with a UTC offset (for example 2026-11-29T16:00:00+01:00). A time without an offset is treated as UTC. mcp.calendar.list returns a JSON array sorted by start time; keep the range narrow.


Architecture

Layer Responsibility Components
Agent Loop Per-request reconstructed trace & tool turns runner.loop
Job Kernel Declarative Cordis catalog & deterministic execution runner.kernel, runner.cordis_tools
Schedules Manifest-defined care flows & dynamic reminders runner.executor, runner.schedule
Providers Streaming completions & OpenAI-compatible tools runner.providers
Identity Decoupled alias, user, and session directory ~/.agents/identity.json
  • Per-request loop (runner.loop): Reconstructs conversation traces on demand. Cordis modules (runner/modules/*.json) map as tools into OpenAI-compatible tool calling rounds.
  • Strict Layering (Cordis Principle): The executor owns no domain state or complex semantics. It strictly executes declared CLI verbs and inspects exit codes (0 = healthy).
  • Durable Manifests (Koru Principle): Scheduled tasks are declared in runner/schedules/*.json. Dynamic reminders are managed via runner.schedule.
  • Zero Bloat: Pure Python standard library (subprocess, json, pathlib, urllib).

Capabilities & Roadmap

Core Capabilities (Implemented)

  • Per-turn Execution Loop (runner.loop): Stateless reconstructed conversation trace per turn.
  • Cordis CLI Tool Round: Declared CLI verbs (runner/modules/*.json) mapped as OpenAI-compatible function tools.
  • Job Kernel (runner.kernel): Declarative AST checking, mixing, and deterministic term reduction (norm, konst, comp, app).
  • Koru Schedules (runner.executor): Declarative scheduled flow manifests (runner/schedules/*.json) with exit code health semantics (exit 0 = healthy).
  • Dynamic Reminders (runner.schedule): Dynamic one-shot, cron, and routine jobs. tick() is in-process safe (file lock, timezone, grace window) and still callable from host cron.
  • Approval gate: AGENTS_APPROVAL_CMD / AGENTS_APPROVAL_MODE before mutating call_job tools.
  • Untrusted tool fence: tool results are wrapped for the model; redaction stays.
  • External MCP client: ~/.agents/mcp.json tools show up as mcp.<server>.<tool>.
  • Skills: skill.list / skill.load read ~/.agents/skills/*/SKILL.md.
  • Attachments: --attach sends images as vision parts when vision is enabled.
  • Provider Streaming: Streaming completions with prefill (first_byte_sec) and stall (idle_sec) hang detection.
  • Decoupled Identity: Decoupled alias (telegram:123), user (u_...), and session (ses_...) directory.
  • FastMCP Interface (runner.mcp_server): Exposes list_catalog, load_schema, and call_job for MCP hosts.
  • Zero Bloat Runtime: Pure Python standard library only (no Celery, no Redis, no cron daemons).

Planned (Roadmap)

  • Formal Kernel Verification: Lean 4 formalization export of Cordis job combinators.
  • Decentralized Identity (DID): Native Ed25519 did:key resolution and proof exchange.
  • Multi-Agent Orchestration: A2A peer execution mesh with cryptographically signed task mailboxes.
  • Adaptive Cadence: Self-tuning care flow intervals based on host execution history.

CLI & Modules

Command / Entrypoint Description
agents-harness (runner.executor) Run and validate scheduled flows (--all, --run <name>, --validate)
agents-harness-loop (runner.loop) Per-request agent loop (--complete, --assemble-only, --call <verb>)
agents-harness-kernel (runner.kernel) Inspect and reduce declarative Cordis job terms (--alphabet, --check, --reduce)
agents-harness-mcp (runner.mcp_server) FastMCP server exposing list_catalog, load_schema, and call_job
python -m runner.schedule Dynamic schedule runner (add, list, remove, tick)

Example Commands

# Validate all scheduled manifests
python -m runner.executor --validate

# Run all scheduled tasks once
python -m runner.executor --all

# List discovered Cordis modules and tool bindings
python -m runner.loop --list-modules
python -m runner.loop --list-tools
python -m runner.loop --list-providers

# Assemble a turn (context + prompt) without calling provider
python -m runner.loop --user 123 --message "hello" --assemble-only

# Complete request with a provider
python -m runner.loop --user probe --message "status report" --complete --provider openai.default

# Tick dynamic due reminders (also safe to call in-process from klanker serve)
python -m runner.schedule tick

# Assemble a turn with an image (vision parts when AGENTS_VISION=1)
python -m runner.loop --user 123 --message "what is this?" --attach ./shot.png --assemble-only

Approval, skills, schedules, attachments

Mutating call_job tools ask first when AGENTS_APPROVAL_CMD is set. {user} in that command becomes the turn's channel user. AGENTS_APPROVAL_MODE=ask gates mutators (the default once a command is set). Creating or listing a schedule does not ask in ask mode; removing one does. strict gates every tool that is not read-only, including schedule create. off, or no command, keeps the old behavior. Exit 0 approves, 1 denies, 2 is timeout/unavailable (denied). A denial tells the model not to retry.

Every tool result the model sees is wrapped in <untrusted_data source="...">...</untrusted_data> after redaction. Only messages the harness builds itself (approval denials, loop notices) stay outside that fence; tool text that merely starts with Denied: is still fenced.

Tool stdout and stderr shown to the model are capped at AGENTS_TOOL_OUTPUT_MAX characters (default 32768). Shorter output is unchanged. Longer output keeps the head and the tail and marks the gap as [... N chars omitted ...]. The same cap applies to schedule tick results and to call_job on the harness MCP server. Trace records (logs/runner.jsonl and ~/.agents/traces/runner.jsonl) still store only a 1000-character tail. The full strings are on the in-process execute_job result (stdout, stderr).

Skills live in ~/.agents/skills/<name>/SKILL.md (AGENTS_SKILLS_DIR, plus AGENTS_SKILLS_EXTRA). The system prompt lists name and description. skill.load returns the file. skill.catalog is a compatibility alias of skill.list.

Cron uses the job's timezone (else AGENTS_TIMEZONE, TZ, or timezone in ~/.agents/config.json). The last fired minute is stored next to the manifests so a double tick does not double-fire, and a miss inside grace_min (default 5) still runs once. LLM reminders and routines default to timeout_sec 300. tick() holds its file lock only while claiming or finalizing, then runs jobs outside the lock. tick(wait=False) returns after the claim; python -m runner.schedule tick waits. A failed or timed-out one-shot is removed and not retried. The failure is logged, and last_result stays in tick-state.json. Routine jobs (--prompt) are not executed here: register runner.schedule.register_routine_handler. With no handler, a routine that has a verb runs that verb; a routine with only a prompt is skipped and its slot or one-shot file is left in place. mcp.schedule.add copies the turn's channel and user onto the job when the call omits them.

python -m runner.loop --detached-session --session ses_… runs that turn in ses_… and restores the previous active session afterward. A session id that starts with routine: is detached the same way without the flag. A host cron or a platform scheduler can call python -m runner.schedule tick when it runs as the same user as the service. That process does not inherit environment set only on the long-running service (for example AGENTS_APPROVAL_CMD).

Variable Default Effect
AGENTS_TOOL_OUTPUT_MAX 32768 Max characters of a tool result shown to the model or a schedule caller. Over the cap, the middle is omitted and marked
AGENTS_APPROVAL_TIMEOUT --timeout in the command + 15 s, else 330 s Seconds to wait for the approval command before denying
AGENTS_SCHEDULE_GRACE_MIN 5 Minutes a missed cron slot still runs once (a job's grace_min wins)
AGENTS_SCHEDULES_DIR ~/.agents/schedules Dynamic reminder manifests; also an overlay for runner.executor schedules
AGENTS_BUNDLED_SCHEDULES 1 0/false/no/off hides the bundled runner/schedules/ from runner.executor

External MCP client

~/.agents/mcp.json (override AGENTS_MCP_CONFIG) uses the Claude/Cursor mcpServers shape, plus optional allow / deny globs. ${ENV_VAR} expands in commands, args, env, urls, and headers. See mcp.client.json.example.

Each server tool is a catalog module mcp.<server>.<tool> (kind: mcp_remote) with the server's inputSchema. readOnlyHint: true is non-mutating; anything else is a mutator for the approval gate. Hand-written modules of the same name win.

The loop process is new every turn. Schemas are cached for AGENTS_MCP_CACHE_SEC (default 60) in AGENTS_MCP_CACHE (default ~/.agents/mcp-tools-cache.json). AGENTS_MCP_CONNECT_TIMEOUT (default 10 s) bounds each listing or call session, connect included. Each call connects, initializes, calls one tool, and disconnects (stdio, streamable HTTP, or SSE). Install the SDK with pip install "agents-harness[mcp]". OAuth is out of scope.

MCP Server Integration

To use the harness as an MCP server for Claude, Cursor, or Antigravity:

{
  "mcpServers": {
    "agents-harness": {
      "command": "python",
      "args": ["-m", "runner.mcp_server"]
    }
  }
}

Exposes:

  • list_catalog: List Cordis job modules available on this machine.
  • load_schema: Load parameter schema for a catalog module.
  • call_job: Safely execute a catalog module by name (unknown names fail closed).

Tests

# Validate manifests
python -m runner.executor --validate

# Run test suite
python -m unittest discover tests

License

MIT. See LICENSE.

Metadata

Release files for agents-harness 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for agents-harness 0.1.1
File Size Uploaded Provenance
agents_harness-0.1.1.tar.gz 81.6 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for agents-harness 0.1.1
File Interpreter ABI Platform
agents_harness-0.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 162.7 kB

Release files / agents_harness-0.1.1.tar.gz

Download URL agents_harness-0.1.1.tar.gz
Size 81.6 kB
Tags Source
SHA-256 checksum
How to use checksums
f150d4d22f07494877a7a9aa137ddd8f41fb0ebc2e28bda9e0c6fb96216f7366
BLAKE2b-256 checksum
How to use checksums
9ed7ee1d0a031bc309fd1ba18f4ef395d037813ddef1c6b466d862c99ae9a413
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.10.6

Release files / agents_harness-0.1.1-py3-none-any.whl

Download URL agents_harness-0.1.1-py3-none-any.whl
Size 81.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
6c04842bb84937a2e72ea0a0f5db70498e47f64c9416f99e09681c70f3611dc6
BLAKE2b-256 checksum
How to use checksums
3ed84f4ed68f2528b0436caf368b66a3f8c80ac280937bf0577741397fed4178
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.10.6

Security for agents-harness 0.1.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page