Skip to content

Wauplin/huggr

Repository files navigation

Huggr

Build a huglet and ship it anywhere as a small, self-contained artifact on a runtime-free, sans-IO Rust core.

Huggr is a toolkit for building small, domain-specific huglets. A huglet is a small Rust crate: a huggr.toml manifest (model tiers, tool grants, limits), a SYSTEM.md system prompt, and optionally a typed Rust response contract. Huggr turns that folder into one standalone binary that answers questions over a JSON contract and serves MCP through --mcp-serve.

The idea is that a specialist with a focused prompt and five jailed tools is cheaper, faster, and safer than a generalist with fifty, and that an orchestrator (a human, a script, or a larger agent) should pay one tool call to use it.

Quickstart

cargo run -p huggr-toolkit --bin huggr -- new my-agent            # scaffold an agent crate
export HF_TOKEN=hf_...                                            # key for the default Hugging Face provider
cargo run -p huggr-toolkit --bin huggr -- run my-agent "question" # interpret it (dev loop)
cargo run -p huggr-toolkit --bin huggr -- build my-agent          # ship it: one standalone binary
./my-agent/dist/my-agent-cli/target/debug/my-agent "question"    # answers; --trace <id> resumes; --mcp-serve serves MCP

Every built binary self-describes: --describe (tools, privileges, tiers, pricing, context policy, limits), --config (effective identity, models, grants, skills, runtime args, limits, state paths, and response schema, with secrets omitted), --traces (stored lineage).

Agent state lives under ~/.huggr/<agent-name>/ by default: immutable traces in traces/, per-lineage scratch state in scratch/. Override with HUGGR_AGENT_HOME or HUGGR_HOME.

What an agent crate looks like

my-agent/
  Cargo.toml          # Rust crate metadata; typed contracts and hooks live here
  huggr.toml           # name, required model tier, tool grants + scopes, limits
  SYSTEM.md           # the system prompt
  src/lib.rs          # optional typed response / hooks / custom Rust wiring
[agent]
name = "policy-docs"
description = "Answers questions about the company travel policy."

[models]
default = "powerful"

[tools.fs_read]
root = "./policies"        # read-only, jailed to this folder

The manifest chooses among four stable capability tiers: fast, balanced, powerful, and max. Concrete providers, model ids, and prices normally live once in ~/.huggr/models.toml; the CLI creates that file with usable defaults on first run. A build resolves and embeds the mappings, while a models.toml on the machine running the built artifact overrides the embedded snapshot. See Models, providers, and pricing.

The manifest defines the agent's blast radius and is the document to audit. Grant-driven tools that are not declared are not registered; the per-lineage scratchpad is part of every ask. Unknown keys are hard errors, so a typo cannot silently widen or narrow the grant.

The built-in library includes jailed filesystem reads and writes, restricted or full shell execution, allowlisted web fetch, Exa web search, memory, trace inspection, scratch state, and isolated self-delegation. High-privilege tools remain opt-in grants: restricted shell commands execute without shell syntax, while full shell and full-disk roots explicitly hand sandboxing to the operator. See the built-in capability reference.

What every agent gets

  • One invocation contract. The input is a question string. The output is a structured response with mandatory status, cost, duration, token, and trace-id metadata. Errors are answers (status: "error", exit 0), so callers branch on data instead of exceptions.
  • Resumable and forkable traces. Every completed turn persists an immutable trace. Pass its trace_id back to continue the conversation, or pass an older id to fork a sibling branch. Replay is bit-for-bit deterministic.
  • Sandboxing by construction. An agent registers only its manifest-granted tools plus the universal scratchpad, each jailed to its declared scope. Every agent also gets explicit blob exchange with the caller.
  • Progressively disclosed skills. Add standard SKILL.md folders to skills = [...] in the manifest or pass --skill <path> for one ask. The model sees the skill catalog and loads matching instructions or referenced files through a jailed reader only when needed.
  • Cost accounting. Every response carries cost (from per-tier pricing config), duration, and token counts, folded from the trace. huggr stats aggregates them across runs.
  • Composition. A built Huggr agent is a tool: grant it with [tools.agent.<name>] artifact = "..." and call it like any capability. Delegation never widens privileges, and the child's cost folds into the caller's metadata.
  • Isolated self-delegation. Grant [tools.delegate] when an agent should call itself in a fresh context window. Recursion is depth-capped and child cost folds into the parent.

Example: the reference docs agent

examples/huglet-docs answers questions about a documentation folder. It has no shell, write, or network tools; only the read-only, folder-jailed fs_* family.

export HF_TOKEN=hf_...
cargo run -p huggr-toolkit --bin huggr -- run examples/huglet-docs ./docs "What is the narrow-waist rule?" | jq
{
  "status": "success",
  "response": {
    "response": "The narrow-waist rule is ...",
    "related_documents": [{ "path": "README.md", "url": "https://github.com/Wauplin/huggr/blob/main/docs/README.md" }]
  },
  "trace_id": "1e4f7d0a9b2c3d44",
  "metadata": { "duration_ms": 1234, "tokens_in": 1000, "tokens_out": 200, "cost_micro_usd": 1300, "models": ["zai-org/GLM-5.2:together"], "model_calls": 2, "tool_calls": 3 }
}

The docs folder is runtime config, not a compiled-in scope: the same agent crate runs against ./docs or any other folder, each invocation jailed to the folder it was given. Build it with huggr build examples/huglet-docs to get a standalone binary that any language can call as a subprocess or through --mcp-serve.

Python and TypeScript

The same runtime is available without writing Rust:

Traces use the same huggr-replay format across surfaces. The Rust CLI can verify a trace when an agent crate resolves to its store; the TypeScript runtime also exposes agent.verify().

The core underneath

The runtime is built on huggr-core, a pure, sans-IO, single-threaded reducer over an append-only event log. The brain and host communicate through two enums and two methods:

loop {
    for cmd in brain.poll() {        // drain commands the brain wants performed
        host.perform(cmd);
    }
    let event = host.next_event().await;  // the only await; host-side only
    brain.submit(Envelope::new(host.now(), event)); // pure, instant, no IO
}

All nondeterminism (time, model output, tool results) is injected through time-stamped events, so any session replays bit-for-bit. A trace stores that replay input plus the consolidated durable log and emitted commands. Resume re-folds the log, a fork copies a prefix, and cost is computed from per-op metadata in it. This is what lets the same brain run natively, in Python, and in the browser.

What Huggr is not

  • Not a general-purpose coding or browser agent. Huggr defines the callee side; generalists are usually the orchestrators that call Huggr agents.
  • Not a hosted runtime or marketplace. Huggr ships artifacts; you choose where to run them (locally, CI, a container).
  • Not an agent-to-agent wire protocol. MCP is the adapter for exposing an agent to orchestrators; A2A and others could be added at the edge but are not foundations.
  • Not multimodal-first. Text in, text out, with blob attachments that a specific agent's tools may interpret.
  • Not stable. This is a hobby prototype with no external users. Breaking changes land without deprecation shims or compatibility ceremony.

Repository layout

huggr/
├── crates/
│   ├── huggr-core/          # the sans-IO brain: log, projection, op table, reducer (no tokio, reqwest, or fs)
│   ├── huggr-host/          # native tokio host: driver loop, capability/model registries, MCP client
│   ├── huggr-providers/     # OpenAI-compatible streaming model adapter
│   ├── huggr-replay/        # trace format, content-addressed blob store, replay/verify/inspect
│   ├── huggr-agent/         # huglet runtime: Ask/Answer, resume/fork, scratchpad, blobs, limits, cost
│   ├── huggr-toolkit/       # manifests, the tool library, and the `huggr` CLI (new/run/build/traces/stats/replay/verify)
│   ├── huggr-wasm/          # WASM bindings around huggr-core for browser/JS hosts
│   └── huggr-python/        # PyO3 runtime embedding (built by maturin from bindings/python)
├── bindings/
│   ├── python/             # the `huggr-agents` Python package
│   └── typescript/         # the `huggr-agents` TypeScript package (Node + browser)
├── examples/
│   ├── huglet-docs/          # the reference docs-Q&A agent crate with a typed response contract
│   ├── huglet-weather/       # the beginner agent; source of the `huggr new --template weather` scaffold
│   ├── huglet-insights/      # offline self-improvement agent over traces and feedback
│   ├── huglet-datasmith/     # docs-QA dataset synthesizer with a typed QaDataset contract
│   ├── hf-librarian/       # Python pipeline: datasmith wheel, jailed Hub publisher, judge-graded eval
│   └── chrome-extension/   # a concrete browser host: chrome.* capabilities, side-panel UI, MV3
└── docs/                   # tutorials, task guides, concepts, and reference

Documentation

  • Tutorials teach a surface end to end.
  • Guides cover specific tasks.
  • Concepts explain design and behavior.
  • Reference specifies contracts, configuration, packages, and terminology.

Building and testing

cargo build --workspace
cargo test                  # unit + scripted/determinism + end-to-end tests
cargo clippy --all-targets
cargo fmt --all
cargo tree -p huggr-core     # audit: must stay free of tokio/reqwest/fs

Notable tests: huggr-core/tests (scripted sessions + deterministic replay), huggr-host/tests/end_to_end.rs (real engine, tools, MCP, record/replay/resume), and the ignored slow gates cargo test -p huggr-toolkit --test conformance -- --ignored / --test build_cli -- --ignored (compile a real agent binary and check the in-process crate, built CLI, and MCP surfaces agree; the generated Python, typed Node/browser, and Chrome surfaces are not yet in this gate).

License

Licensed under Apache-2.0.

About

No description, website, or topics provided.

Resources

License

Stars

3 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors