drover

A declarative, strongly-typed, Effect-native agent harness library for TypeScript.

drover is a declarative, strongly-typed, Effect-native agent harness library for TypeScript. Define agents as JSON-serialisable specs. The API surface is Effect-first, with a Promise/AsyncIterable facade as a fallback for non-Effect consumers.

Features

  • Declarative agent specsdefineAgent({...}) returns a JSON-serialisable, TypeBox-typed AgentSpec; dynamic agents use the same shape.
  • Effect-first API — internals and public surface speak Effect; Promise/AsyncIterable facade kept as a fallback.
  • Schemaed agent loop — typed input/output, output retry budget on schema-decode failures.
  • Streaming events — normalised HarnessEvent stream as an AsyncIterable.
  • Plugin hooksHarnessPlugin bundles: beforeToolCall / afterToolCall / wrapTool / onEvent / onError / onRunStart / onRunEnd.
  • Built-in plugins — loop-detect, step-tracer, bash-blocklist, circuit-breaker, write-policy, phase-recorder, confirm-gate, output-validate.
  • Built-in tools — bash, read, write, edit, grep, …
  • SubagentstaskTool factory; depth ≤ 2, fan-out ≤ 3 caps; child lifecycle events on the parent stream.
  • SkillsSKILL.md loader + skill_load tool, progressive disclosure.
  • Commands & lifecycle — markdown prompt macros (host-pushed) run as deterministic init / postSuccess steps around the loop.
  • Instruction filesAGENTS.md / CLAUDE.md loaders.
  • MCP — stdio + HTTP transports, tool-name prefixing, per-agent allowlist.
  • Model layer — pi-ai wrapper, alias resolver, routing interface, circuit breaker.
  • Pause / resume — durable checkpoints; resume validates run id / status / agent id / spec hash before replay.
  • Storage — libsql default + StorageAdapter; in-memory adapter for tests.
  • SandboxSandboxAdapter interface; just-bash virtual sandbox by default (isolated, docker-style mounts, bash-safe), plus in-process none.
  • Runtime (opt-in) — worker pool, lease queue, RunApi, crash recovery.
  • EvalsScenarioRunner, Scorer, Reporter, plus the eval-viewer app.

drover is a small library that hosts LLM agents. You give it an agent spec (prompt, schemas, tools, model), and it runs the loop: validate input, call the model, dispatch tools, decode the output against your schema, retry on schema failures, persist everything if you wire storage, pause / resume durably if you ask it to.

What it ships

Agent loop

Schemaed input + output, pi-agent-core driving the model, output retry budget for schema failures.

Plugin hooks

beforeToolCall / afterToolCall / onEvent / wrapTool bundles. Ship your safety + observability as plugins.

Subagents

taskTool factory. Parent agent spawns scoped children with depth and fan-out caps. Lifecycle events surface on the parent stream.

Storage

Runs / events / checkpoints in libsql. Memory adapter for tests; libsql for durability. Pluggable interface — bring your own.

Pause / resume

handle.pause() checkpoints + suspends. resumeAgent continues from the saved messages via pi’s runAgentLoopContinue.

Skills + MCP

SKILL.md allowlists, progressive disclosure. MCP stdio + HTTP via @modelcontextprotocol/sdk, tool-name prefixing, per-agent allowlist.

Runtime

Worker pool + lease queue + crash recovery. Programmatic RunApi. Point multiple processes at the same libsql queue for horizontal scaling.

Observability

Normalised HarnessEvent stream. Step tracer plugin projects it into a flat list. Eval-viewer renders the timeline.

Design principles

  • Effect-first, Promise/AsyncIterable facade as fallback. The public surface speaks Effect<RunResult, HarnessError, R>. The facade gives non-Effect consumers a Promise that never rejects and an AsyncIterable<HarnessEvent> — adopt without rewriting.
  • Capability flags over docstrings. Sandbox capabilities are typed. Want bash? Pass allowShell: true. Documenting an escape in prose is not a safety boundary.
  • Resume validates everything before replaying. Run id, status, agent id, spec hash. Drift = ResumeError, not a silent replay under a changed policy.
  • Plugins ship only what’s wired. ToolDecision is allow | deny for v0. rewrite and require_confirm come back when storage-backed confirmation lands.

Where it sits

drover wraps @mariozechner/pi-agent-core for the model loop, adds TypeBox-schemaed agent specs + tools, and layers storage / pause-resume / plugin / MCP / skills primitives on top. Effect provides the error channel and composition.

Next

Type to search…

↑↓ navigate open esc close