awf

module
v0.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 7, 2026 License: Apache-2.0

README

awf

CI Release Go Report Card Go Reference Go 1.26 License

Run agents you don't babysit, and trust the result.

awf is a single-binary runtime for agentic workflows that need a real acceptance gate: run the agent, check the result independently, repair from the critique, and resume safely after crashes without redoing committed work.

It is built for workflows where a model saying "done" is not good enough: coding agents, research agents, data-migration agents, support-reply agents, security triage agents, or any pipeline where each stage should advance only after an external check passes.

CLI reference | Workflow format

Why AWF Exists

Agentic workflows are useful because they let models do open-ended work. They are risky for the same reason: the step can report success without actually meeting the requirement.

AWF makes the acceptance check part of the runtime.

flowchart LR
    generate["generate<br/>agent or command"]
    evaluate["evaluate<br/>fresh judge or deterministic check"]
    pass{"passes?"}
    next["next stage"]
    repair["repair with critique"]

    generate --> evaluate --> pass
    pass -->|"yes"| next
    pass -->|"no"| repair --> generate

The central primitive is the gate: a generate block, an independent evaluate block, an until condition over the evaluator's typed output, and a bounded repair loop. The evaluator runs in a fresh context, so the generator never marks its own homework. A crash is not a verdict; only a real evaluation with a false until consumes a repair attempt.

What You Get

  • Independent gates: engine-enforced generate -> evaluate -> repair loops, with the prior verdict automatically fed into the next generate attempt.
  • Black-box agents: wrap existing CLIs such as Claude Code, Factory droid, Block Goose, OpenAI Codex, or use awf/llm for direct OpenAI-compatible HTTP.
  • Typed outputs: downstream steps bind to validated output_schema fields, not fragile free text.
  • Checkpoint/resume: step outputs and declared files commit to a content-addressed artifact store before the journal pointer moves.
  • Stall recovery: an idle watchdog catches a wedged agent (silent longer than timeout.idle, distinct from slow-but-thinking) and turns it into a retryable failure; on a stall a persistent-session step resumes the same conversation thread (recovery: continue) instead of restarting. Default-on for the codex live adapter, which streams a reasoning heartbeat; opt-in elsewhere.
  • Pinned replay: a structural change — topology, imported files, assets, container digests, or resolved runtime versions — hard-fails the resume; an edit confined to step bodies instead reuses the unchanged committed steps and re-runs from the first change.
  • Real workspaces: run against long-lived native processes, digest-pinned containers, or Compose labs; Docker handles networking, healthchecks, and multi-service wiring.
  • Traceable runs: inspect runs, fold status from the log, and export traces without putting observability in the execution path.

A First Workflow

The smallest AWF workflow needs four things: a format version, a graph, a step id, and a command. No container image, no agent credentials, no Ollama server:

version: 1
graph:
  - id: hello
    run: echo "hello from awf"

Save it as hello.yaml and run it:

awf run hello.yaml
awf run: auto-selected native backend (no Docker-only features). Resume restores snapshot: workspace workdirs from a full workdir archive but does not pin the host base environment; use --backend docker for a pinned baseline.
hello from awf
run 1a2b3c4d: ok

(The run id on the last line is minted per run, so yours will differ.)

The step declares no container:, so auto backend selection finds no Docker-only feature to route to and picks native: the command runs directly on the host, write-confined to its own per-step host workspace by an OS sandbox (bubblewrap or Landlock on Linux, sandbox-exec on macOS), and its output is committed to the run's journal — the same checkpoint path a Docker-backed step uses, just with no container boundary. --backend docker refuses a bare run: step outright (AWF1065): there is no image to run it in, so let auto decide, or pass --backend native yourself.

Real workflows are rarely one step, and running a command is not what makes AWF different from a shell script. The primitive that does is the gate: an independent check that decides whether to advance. The next section shows one.

A Gated Workflow

This workflow asks a model to write a release note, then has an independent judge approve it or send feedback into the next repair attempt. Unlike the hello-world above, it needs a running OpenAI-compatible endpoint (Ollama here) and an OPENAI_API_KEY env var — see Quickstart below for the exact commands to run it.

workflow: gated-release-note
version: 1

graph:
  - gate:
      generate:
        - id: draft
          uses: awf/llm
          with:
            base_url: http://localhost:11434/v1
            model: llama3.1
            api_key_env: OPENAI_API_KEY
            system_prompt: "You write concise release notes."
            prompt: |
              Write a three-sentence release note for a new `awf run --json` flag.
              Prior review feedback, if any: {{ evaluate.feedback }}
          output_schema:
            type: object
            additionalProperties: false
            required: [release_note]
            properties:
              release_note: { type: string }

      evaluate:
        - id: judge
          uses: awf/llm
          with:
            base_url: http://localhost:11434/v1
            model: llama3.1
            api_key_env: OPENAI_API_KEY
            system_prompt: "You are a strict release-note reviewer."
            prompt: |
              Review this release note:

              {{ step.draft.release_note }}

              Approve it only if it is accurate, specific, and exactly three sentences.
          output_schema:
            type: object
            additionalProperties: false
            required: [approved, feedback]
            properties:
              approved: { type: boolean }
              feedback: { type: string }

      until: "{{ evaluate.approved }}"
      max_attempts: 3

That same shape works for higher-stakes tasks: generate a patch and run tests, draft a customer reply and judge it against account data, triage a CVE and check the exploitability claim, or migrate data and verify the target state.

Install

Download the archive for your platform from the Releases page, verify it against the published checksums, and put awf on your PATH. Prebuilt binaries are published for linux/amd64, linux/arm64, darwin/amd64, and darwin/arm64.

VERSION=0.1.0
OS=linux ARCH=amd64        # or: OS=darwin ARCH=arm64
BASE="https://github.com/valbaudo/awf/releases/download/v${VERSION}"

curl -LO "${BASE}/awf_${VERSION}_${OS}_${ARCH}.tar.gz"
curl -LO "${BASE}/awf_${VERSION}_checksums.txt"
shasum -a 256 -c "awf_${VERSION}_checksums.txt" --ignore-missing

tar -xzf "awf_${VERSION}_${OS}_${ARCH}.tar.gz"
sudo install "awf_${VERSION}_${OS}_${ARCH}/awf" /usr/local/bin/awf
awf version

Release archives are built by GitHub Actions and carry a build-provenance attestation. You can verify one with the GitHub CLI:

gh attestation verify "awf_${VERSION}_${OS}_${ARCH}.tar.gz" --repo valbaudo/awf
Go install

With a Go 1.26+ toolchain:

go install github.com/valbaudo/awf/cmd/awf@latest

Quickstart

To build from source instead — the path for contributors:

git clone https://github.com/valbaudo/awf.git
cd awf
make build

Validate a workflow without running agents, containers, or network I/O:

bin/awf validate examples/awf-llm-ollama/workflow.yaml

Run the local Ollama example after starting an OpenAI-compatible Ollama server and forwarding an API-key env var. Ollama can ignore the bearer value; AWF still requires the named env var to be present because adapters never inline secrets into workflow files.

export OPENAI_API_KEY=ollama
bin/awf run examples/awf-llm-ollama/workflow.yaml

Use Docker when you want resumable runs or workflows that need isolated containers/Compose labs:

bin/awf run --backend docker path/to/workflow.yaml
bin/awf resume <run-id> path/to/workflow.yaml

Useful development checks:

make lint test        # pre-commit bar
make build            # build ./bin/awf
make integ            # Docker/native integration suite; no live API spend
Cutting a release (maintainers)

Releases are tag-triggered. Push an annotated, signed vMAJOR.MINOR.PATCH tag; GitHub Actions then re-runs the full gate, builds the four platform archives, and publishes the GitHub Release with checksums and a build-provenance attestation:

git tag -s v0.1.0 -m "v0.1.0"
git push origin v0.1.0

Published version tags are immutable: fix a bad release with the next patch tag (v0.1.1), never by moving or deleting a tag. See CONTRIBUTING.md for the full contributor bar.

How AWF Is Different

Concern Common agent framework shape AWF shape
Model execution Framework calls the model in-process Runtime wraps external CLIs or one HTTP LLM call as black boxes
Quality check Author wires evaluator nodes or post-hoc evals Engine owns the independent gate and repair loop
State Mutable snapshots or app-managed memory Append-only journal plus content-addressed artifacts
Resume Recompute, restore app state, or apply latest definition Replay committed steps, rerun only the uncommitted frontier
Drift Often latest-wins Definition digest and runtime versions are pinned; drift is a hard error
Infra Usually in-process or platform-owned workers Single-host native/Docker/Compose workspaces, rebuilt from pinned recipes

AWF is not trying to be a distributed scheduler, a durable-execution platform, or a general agent-team framework. It is a narrow runtime for single-host, checkpointed agentic pipelines where every meaningful stage has to pass an independent check before the workflow can move on.

Core Concepts

  • Workflow: a YAML document with input schema, optional assets/imports, execution infrastructure, and a graph of steps/control flow. The resolved document is content-addressed at run start. Its version: 1 field is the workflow-format version and is independent of the awf tool version — awf v0.1.0 implements workflow format version 1.
  • Step: a run: command, uses: agent invocation, await signal, or imported workflow call. Steps can produce typed JSON outputs and named output files.
  • Gate: a control node that runs generate steps, evaluates them independently, checks until, and repairs with feedback until the condition passes or max_attempts is exhausted.
  • Commit: the durable unit of progress. AWF writes typed outputs, declared files, and optional workspace snapshots to the blob store first; only then does it append the completed journal event.
  • Resume: a fold over the journal. Completed steps are replayed from committed artifacts; only the uncommitted frontier re-executes. A structural or runtime-version change stops the resume instead of silently adapting; an edit confined to step bodies reuses unchanged committed steps and re-runs from the first change (per-node verifying-trace reuse).

Adapters and Backends

Agent steps name a runtime with uses:. The step's with: map is opaque to the engine; only the named adapter validates and interprets it.

Built-in adapters:

  • anthropic/claude-code
  • factory/droid
  • block/goose
  • openai/codex
  • awf/llm for OpenAI-compatible Chat Completions endpoints, including local Ollama, vLLM, llama.cpp, LM Studio, LiteLLM, and Bifrost-style gateways
  • openai/codex-live for Codex app-server-backed live sessions

The openai/codex-live adapter uses the same uses: resolution, runtime pinning, live event stream, trace, and UI surfaces as other agent steps. It stores provider session metadata under the live home and keeps raw live transcripts provider-owned. block/goose-live remains a reserved implementation track ref, and anthropic/claude-code-live remains deferred behind a PTY proof spike.

Execution backends:

  • native: host processes, fastest path; no container boundary, but each step is write-confined by an OS sandbox (bubblewrap or Landlock on Linux, sandbox-exec on macOS) so it can write only to its per-run workspace and TMPDIR — fail-closed, with a loud stderr warning if no sandbox tool is present; resumable (snapshot: workspace workdirs are restored on resume). Explicit --backend native runs image-mode workflows on the host, ignoring the declared image; the host base environment is not pinned — use --backend docker for a fully reproducible baseline
  • docker: digest-pinned images and Compose projects, resumable
  • fake: in-memory backend for conformance tests

See awf(1) for adapter environment variables, streaming notes, security caveats, and CLI flags.

Documentation

  • awf(1): command reference, flags, exit status, environment, tracing, and examples.
  • awf-workflow(5): the workflow-format reference and stable contract for fields, control flow, templating, typed outputs, and checkpoint/resume.
  • examples/: runnable examples for awf/llm, droid BYOK, and engine-owned conversation threads.

Further Reading

These are the sources AWF actually draws from. The list is intentionally narrow.

Runtime foundations

Directories

Path Synopsis
Package agent provides the Adapter interface — the seam between the engine's dispatcher and external agent CLIs (Claude Code first, per awf-workflow(5) (Agent step) and runtime-design.md §8).
Package agent provides the Adapter interface — the seam between the engine's dispatcher and external agent CLIs (Claude Code first, per awf-workflow(5) (Agent step) and runtime-design.md §8).
awfllm
Package awfllm implements agent.Adapter as a single, streaming LLM call against any OpenAI-compatible Chat Completions endpoint (OpenAI, Ollama, vLLM, llama.cpp, LM Studio, LiteLLM/Bifrost gateways).
Package awfllm implements agent.Adapter as a single, streaming LLM call against any OpenAI-compatible Chat Completions endpoint (OpenAI, Ollama, vLLM, llama.cpp, LM Studio, LiteLLM/Bifrost gateways).
claude
Package claude implements agent.Adapter against the Claude Code CLI.
Package claude implements agent.Adapter against the Claude Code CLI.
claudesession
Package claudesession implements agent.Adapter for Claude Code with deterministic session-id reuse (anthropic/claude-code-session).
Package claudesession implements agent.Adapter for Claude Code with deterministic session-id reuse (anthropic/claude-code-session).
codex
Package codex implements agent.Adapter against OpenAI's `codex` CLI (the `codex exec` non-interactive subcommand).
Package codex implements agent.Adapter against OpenAI's `codex` CLI (the `codex exec` non-interactive subcommand).
droid
Package droid implements agent.Adapter against Factory AI's `droid` CLI (the `droid exec` non-interactive subcommand).
Package droid implements agent.Adapter against Factory AI's `droid` CLI (the `droid exec` non-interactive subcommand).
fake
Package fake provides an in-memory scripted agent.Adapter implementation.
Package fake provides an in-memory scripted agent.Adapter implementation.
goose
Package goose implements agent.Adapter against Block's `goose` CLI (the `goose run` non-interactive subcommand).
Package goose implements agent.Adapter against Block's `goose` CLI (the `goose run` non-interactive subcommand).
Package cli assembles the command-line surface.
Package cli assembles the command-line surface.
Package clock provides the Clock and IDGen interfaces, injected wherever time/ids are needed.
Package clock provides the Clock and IDGen interfaces, injected wherever time/ids are needed.
cmd
awf command
Command awf is the AWF runtime CLI entry point.
Command awf is the AWF runtime CLI entry point.
genrates command
Command genrates regenerates pricing/rates.json from the models.dev pricing database (with a LiteLLM fallback), filtered to a curated allowlist of the models AWF prices.
Command genrates regenerates pricing/rates.json from the models.dev pricing database (with a LiteLLM fallback), filtered to a curated allowlist of the models AWF prices.
Package conformance is the Backend-parameterized test suite the design spec §H calls "the definition of done" for Phase 2 onward.
Package conformance is the Backend-parameterized test suite the design spec §H calls "the definition of done" for Phase 2 onward.
Package container provides the Backend seam — the interface the engine's Dispatcher uses to run commands inside long-lived containers (a single digest-pinned image or a compose project, per awf-workflow(5), CONTAINERS).
Package container provides the Backend seam — the interface the engine's Dispatcher uses to run commands inside long-lived containers (a single digest-pinned image or a compose project, per awf-workflow(5), CONTAINERS).
backendtest
Package backendtest is the parameterized interface-conformance test for container.Backend.
Package backendtest is the parameterized interface-conformance test for container.Backend.
docker
Package docker implements container.Backend against the Docker Engine SDK.
Package docker implements container.Backend against the Docker Engine SDK.
native
Package native implements container.Backend by running commands directly on the host via os/exec.
Package native implements container.Backend by running commands directly on the host via os/exec.
engine/local_dispatcher_agent.go — the AgentStep dispatch path.
engine/local_dispatcher_agent.go — the AgentStep dispatch path.
frontend
yaml
Package yaml is the YAML frontend: parses YAML workflows into the ir.Workflow IR.
Package yaml is the YAML frontend: parses YAML workflows into the ir.Workflow IR.
Package graph projects a workflow into a node/edge graph for visualization — the JSON contract behind a future visual graph tool (`awf graph --json`).
Package graph projects a workflow into a node/edge graph for visualization — the JSON contract behind a future visual graph tool (`awf graph --json`).
Package ir provides stable IR types (the contract), structural validation, and the definition digest.
Package ir provides stable IR types (the contract), structural validation, and the definition digest.
Package loader reads workflow YAML files and their referenced compose files/assets into an ir.LoadedDefinition.
Package loader reads workflow YAML files and their referenced compose files/assets into an ir.LoadedDefinition.
Package obs is the read-only OpenTelemetry projection of the AWF event log.
Package obs is the read-only OpenTelemetry projection of the AWF event log.
Package pricing converts normalized token counts into a per-model USD cost.
Package pricing converts normalized token counts into a per-model USD cost.
Package release holds repository-contract tests that guard the release pipeline: the tag-triggered GitHub Actions workflow and the README's install and maintainer-release instructions.
Package release holds repository-contract tests that guard the release pipeline: the tag-triggered GitHub Actions workflow and the README's install and maintainer-release instructions.
Package retry provides retry policy, backoff math, and per-policy exit-code classification — the data primitive that engine.RunWithRetry composes with a Dispatcher + Clock into the retry loop.
Package retry provides retry policy, backoff math, and per-policy exit-code classification — the data primitive that engine.RunWithRetry composes with a Dispatcher + Clock into the retry loop.
Package runlock is the sidecar run-liveness lock: an exclusive BSD flock(2) held by `awf run` / `awf resume` for a run's lifetime, plus a non-blocking shared probe (Held) that distinguishes a live run (lock held) from a crashed one (lock free).
Package runlock is the sidecar run-liveness lock: an exclusive BSD flock(2) held by `awf run` / `awf resume` for a run's lifetime, plus a non-blocking shared probe (Held) that distinguishes a live run (lock held) from a crashed one (lock free).
Package signal is the cross-process control-surface for `awf signal` / `awf pause` / `awf cancel`.
Package signal is the cross-process control-surface for `awf signal` / `awf pause` / `awf cancel`.
Package state provides the durability core — an append-only Log and content-addressed Blobs — that the engine (Phase 2) sits on for commit/resume and that obs (Phase 6) reads to project OTel spans.
Package state provides the durability core — an append-only Log and content-addressed Blobs — that the engine (Phase 2) sits on for commit/resume and that obs (Phase 6) reads to project OTel spans.
Package template implements the §7 mini-language: lexer + recursive-descent parser for the bounded expression grammar from the Phase 1 design spec §B, plus reference extraction over the resulting AST and a `{{ … }}` slot scanner for substitution-bearing host strings.
Package template implements the §7 mini-language: lexer + recursive-descent parser for the bounded expression grammar from the Phase 1 design spec §B, plus reference extraction over the resulting AST and a `{{ … }}` slot scanner for substitution-bearing host strings.
Package ui serves the awf visual graph tool: a localhost HTTP server that renders a workflow's graph (via the Slice-1 graph package) and overlays run state.
Package ui serves the awf visual graph tool: a localhost HTTP server that renders a workflow's graph (via the Slice-1 graph package) and overlays run state.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL