New:Introducing Socket Scanning for VS Code Marketplace Extensions.Learn more →
Get Started

leharness

Package Overview
Dependencies
Maintainers
1
Versions
6
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

leharness

Experimental agent harness CLI.

latest
npmnpm
Version
0.4.0
Version published
Maintainers
1
Created
Source

leharness banner

leharness

leharness is me trying to understand harness engineering by building one myself.

I mostly want to understand the parts that actually matter underneath modern harnesses:

  • the loop
  • the event log
  • the tool runtime
  • the task model
  • background vs blocking execution
  • subagents
  • compaction
  • and the products that can grow on top of that core

Also, the name exists because I dreamt up the project while I was in Paris.

CLI

The CLI is experimental and published as leharness on npm. It installs one command: lh.

Install globally:

npm install -g leharness
lh --help

Run without installing:

npx leharness@latest --help

Update an existing global install:

npm install -g leharness@latest

Start an interactive session:

lh

Run one prompt and exit:

lh "summarize this repo"

Use OpenAI:

export OPENAI_API_KEY=...
lh --provider openai

Use DeepSeek:

export DEEPSEEK_API_KEY=...
lh --provider deepseek
lh --provider deepseek --model deepseek-v4-pro

Use Ollama:

ollama pull qwen3.6:27b-coding-nvfp4
lh --provider ollama

In the TUI, /model opens a client-side model picker and /effort opens a client-side reasoning effort picker for supported models:

/model
/effort

See lh --help for the full set of options and environment variables.

The CLI also reads a repo-local .env file before provider setup.

Sessions are saved under .leharness/sessions in the current working directory unless LEHARNESS_HOME is set. Workspace-owned skills can live under .leharness/skills/<name>/SKILL.md; inherited workspace skill locations .agents/skills and .claude/skills are also discovered.

For local package development:

pnpm install
pnpm package:verify

Why

Most of the interesting agent repos mix together:

  • a harness kernel
  • product surfaces
  • UI/TUI
  • routing and integrations
  • a lot of operational scar tissue

That is useful if you want the whole product, but it makes it harder to study the lower-level harness decisions cleanly.

So this repo is basically me taking notes, doing comparative research, and hopefully ending up with a core I can improve one feature at a time without having to keep rewriting the foundation.

High-Level Goals

  • Build a small, explicit agent loop that stays easy to reason about.
  • Use append-only event logs as the canonical session state.
  • Persist important state and large outputs to the filesystem whenever possible.
  • Treat long-running work as a first-class concept instead of a shell-only hack.
  • Support isolated subagents and background work without turning the core into spaghetti.
  • Keep the harness channel-agnostic so CLI, web, TUI, bots, or VM runners can all sit on top of the same engine.
  • Make the system easy to revisit and improve in bursts instead of requiring rewrites every time a new feature appears.

Core Bets

These are the main architectural bets I want the base layer to rest on:

At the boundary, the flow should look like:

ingress -> invocation -> append invocation events -> run session loop
  • Simple parent loop One clear control loop that stays small, readable, and focused on orchestration.

    while (true) {
      const events = loadEvents(sessionId)
      const session = projectSession(events)
    
      if (shouldCompact(session)) {
        compact(session)
        continue
      }
    
      const prompt = buildPrompt(session)
      const modelOutput = await callModel(prompt)
      const toolResults = await executeToolCalls(session, modelOutput.toolCalls)
    
      if (!shouldContinue(session, modelOutput, toolResults)) break
    }
    
  • Generic core, thin wrappers The harness core should stay generic. CLI, coding-agent defaults, web/TUI, bots, and future products should sit on top instead of leaking into the loop.

  • Event-sourced sessions The event log should be the source of truth for what happened in a session.

    {"type":"invocation.received","kind":"message","text":"fix the failing test"}
    {"type":"step.started","step_id":"step_1"}
    {"type":"model.completed","tool_calls":[{"tool":"bash","execution":"auto"}]}
    {"type":"task.started","task_id":"task_42","kind":"bash"}
    {"type":"task.completed","task_id":"task_42","summary":"2 tests still failing"}
    
  • Session derived from events The session should be rebuilt from events, and everything else should be derived from that session.

    const session = projectSession(events)
    const prompt = buildPrompt(session)
    const notifications = projectTaskNotifications(session)
    const artifacts = projectArtifacts(session)
    
  • Background as a first-class runtime feature The agent should be able to send work off, keep moving, and react when completions come back. That means task-like operations should be able to finish inline or return a durable handle when they need to keep running.

    const testRun = await bash({
      command: "npm test",
      execution: "auto",
    })
    
    // inline:
    // { status: "completed", output: "..." }
    
    // background:
    // { status: "started", task_id: "task_42" }
    
    onTaskCompleted(task) {
      appendEvent({
        type: "task.completed",
        task_id: task.id,
        session_id: task.sessionId,
      })
    
      markSessionRunnable(task.sessionId)
    }
    
  • Isolated subagents Child runs should have bounded scope, inspectable state, and a clear handoff back to the parent.

    const child = await spawnSubagent({
      session_id: session.id,
      prompt: "investigate the lint failures",
      execution: "background",
    })
    
    // later:
    // waitTask(child.task_id)
    // or react when completion is projected back into the parent session
    
  • Filesystem-backed artifacts Big outputs should live on disk with stable references so they can be revisited later without bloating active context.

    const artifact = await persistArtifact({
      kind: "tool_output",
      content: stdout,
    })
    
    appendEvent({
      type: "artifact.created",
      artifact_id: artifact.id,
      path: artifact.path,
    })
    

Status

The kernel — loop, event log, tool runtime, background tasks, subagents, artifacts, compaction — is built. Per-feature design docs live in plans/.

What's Next

  • web inspector
  • coding-agent wrapper
  • MCP integration
  • tool-agnostic kernel: the bigger built-in features (tasks, subagents, artifacts, skills) currently ship their model-facing tools inside the kernel. Pull those into a modular "default tools" layer over the kernel services, so the core loop carries no opinions about which tools exist
  • branchable session history
  • VM runners
  • Telegram or other bot adapters
  • more opinionated agent products built on top of the same harness

Direct Inspiration

These are the repos I've been reading against while trying to figure out what I actually want the core of leharness to be:

North Star

I want a harness core that is:

  • simple enough to explain
  • modular enough to evolve
  • durable enough to resume
  • and strong enough that future work feels like adding a feature, not rebuilding the foundation

AI Tools Used

These were the main AI tools I used while doing the research and writing in this repo:

FAQs

Package last updated on 28 May 2026

Related posts