Agent = Provider + Loop + Tools.
In Go.
glue is a small, provider-agnostic agent harness — and the reference agents built on it. A reusable loop, typed tools, four model providers, opt-in persistence and search, all behind a small code-first API.
go get github.com/erain/glue An agent loop, code-first, in Go.
Most agent frameworks bundle a model, a chain DSL, a deploy target,
and a runtime opinion. glue separates concerns: a reusable
provider-agnostic loop, a small Agent /
Session API, four pluggable providers, and
ready-to-register tool bundles. The whole framework fits in your
head.
Inspired by pi-mono and flue: same conceptual shape, in Go. Single static binary, no runtime, no Python/Node version manager, no Docker just to run an agent.
The architectural rule (ADR-0005): every product concern enters glue only as an interface the host fills in. Sandboxing, channels, scheduling, permission policy — your code, not glue’s. That’s how the core stays small while real agents grow on top of it.
Provider, session, prompt.
package main
import (
"context"
"fmt"
"log"
"github.com/erain/glue"
"github.com/erain/glue/providers/gemini"
)
func main() {
ctx := context.Background()
agent := glue.NewAgent(glue.AgentOptions{
Provider: gemini.New(gemini.Options{}), // reads GEMINI_API_KEY
Model: "gemini-2.5-flash",
})
session, err := agent.Session(ctx, "demo")
if err != nil {
log.Fatal(err)
}
result, err := session.Prompt(ctx, "Reply with the single word: glue.")
if err != nil {
log.Fatal(err)
}
fmt.Println(result.Text)
}
The session keeps an in-memory transcript, so a second
session.Prompt(...) continues the conversation. Pass
AgentOptions.Store (e.g. stores/file or
stores/sqlite) and transcripts survive across processes.
Other providers — Codex, NVIDIA, OpenRouter — are one import away.
Seven types, one loop.
Once these click, the rest is API surface:
- Provider — a model backend that streams assistant events.
- Agent — provider, model, tools, store, work dir, roles. Built with
glue.NewAgent. - Session — a named conversation with its own transcript; driven with
session.Prompt. - Tool — a function the model can call. Defined with
glue.NewTool[Args]. - Store — where transcripts persist (
stores/fileorstores/sqlite). Optional. - Skill / Role — Markdown-driven reusable instructions and named instruction profiles.
- loop — the engine: stream → run tools → append results → repeat until the model stops.
Every session.Prompt runs the same flow:
prompt ─▶ provider streams events ─▶ text? emit deltas
└─▶ tool calls? run tools, append, loop
└─▶ stop ─▶ return final text Batteries included, nothing welded shut.
Four providers + failover
Codex (ChatGPT subscription), Gemini, NVIDIA build, OpenRouter. A driver-style registry + glue.WithFailover.
Typed tools
glue.NewTool[Args] decodes JSON into your Go type before the executor runs; failures surface as ErrorResult, not panics.
Subagents
glue.SubagentTool wraps a child Agent as a tool — fresh, isolated transcripts for delegated work.
Persistence + FTS5 search
stores/file for the simple default; stores/sqlite for cross-session recall via pure-Go FTS5.
Streaming
WithStreamWriter, WithToolLogger, Session.Subscribe — compose additively, never crowd each other out.
Skills & roles
AGENTS.md, .agents/skills/<name>/SKILL.md, and roles/*.md discovered from your WorkDir. Frontmatter sets model overrides.
Coding-agent bundle
read_file, write_file, edit_file, list_dir, find_files, grep, shell_exec, git_diff_branch — permission-gated where it matters.
MCP client
Consume Model Context Protocol servers (stdio / Streamable HTTP) as permission-gated glue.Tool values.
Local daemon + channels
glue serve / glue connect over HTTP+SSE. Telegram and other channels plug in as adapters in your agent package.
Each capability is opt-in. The core glue package
doesn’t import any provider, store, or tool by default —
adding them is an explicit import in your
main.go.
From minimal to packaged, in one guide.
The end-to-end guide walks you from a 15-line minimal agent to a packaged, tested CLI in ten steps: typed tools, persistence, streaming, project context, subagents, structured output, multi-provider failover, packaging, and testing with a fake provider.
Agents that ship a binary share six standard flags
(--provider, --model, --id,
--store, --work, --max-turns):
import (
"flag"
"github.com/erain/glue/cli"
)
fs := flag.NewFlagSet("my-agent", flag.ContinueOnError)
get := cli.RegisterStandardFlags(fs, nil)
_ = fs.Parse(os.Args[1:])
cfg := get() // cfg.Provider, cfg.Model, cfg.ID, cfg.Store, cfg.Work, cfg.MaxTurns Two reference agents, in real use.
glue-review
A free, local pre-push branch reviewer. Reads the diff against
main, deep-reads files when needed, and posts one
sticky GitHub comment per PR — a short headline, severity bullets,
and a fenced ```markdown fix block downstream coding
agents (Claude Code, Codex, Cursor, Aider, …) paste in one step.
Runs as a CLI or a GitHub Action.
peggy
A long-running personal-assistant agent: CLI, Telegram, and a shared HTTP+SSE daemon; durable sqlite+FTS5 memory with curated recall; opt-in local coding tools; MCP servers; scheduled/proactive runs; per-channel permission tiers. The best reference for a feature-rich agent built on glue.
Pre-1.0, in active use.
Anticipated questions.
How is glue different from flue?
Same conceptual shape — pi-mono-style reusable loop, small core, real agents on top — but Go instead of TypeScript, local-first (no platform fees), and shipped with reference agents that exercise the long-running primitives (memory, channels, scheduling, permissions) rather than just demoing the loop.
How is it different from langchaingo or other Go agent libraries?
glue is smaller and more opinionated. The whole framework fits in your head: provider, loop, session, tool. There’s no chain DSL, no graph compiler, no hidden global state. Trade-off: you write a little more glue (sorry) for the orchestration you want, in plain Go.
Why Go?
Single static binary. No runtime, no version managers, no Docker just to run an agent. Deploys to anything that takes a binary. Goroutines map cleanly to streaming + tool execution.
Is the Codex provider really supported?
It uses your ChatGPT-subscription auth via codex login,
not an OpenAI API key. Subscription-auth via third-party tools
is not formally documented by OpenAI; the provider is intended
for personal use. Other providers (Gemini, NVIDIA, OpenRouter)
use standard API keys.
API stability?
Pre-1.0. The public Agent / Session /
Tool surface is stable in practice — it has
survived ~30 PRs without breaking changes — but we reserve the
right to break API on minor bumps until 1.0.0.
See CHANGELOG.md.
Do I need an API key?
Yes for the network providers (GEMINI_API_KEY,
NVIDIA_API_KEY, OPENROUTER_API_KEY).
The Codex provider uses codex login. The
openrouter/free meta-route can run free.