A minimal, composable Go library for building agentic tool-calling loops
Go
5
18 commits
updated Jul 19, 2026
A minimal, composable Go library for building agentic tool-calling loops on top of LLM APIs.
1.26+result, err := harness.Run(ctx, provider,
harness.WithSystem("You are a helpful assistant."),
harness.WithMessages(thread.Messages...),
harness.WithTools(tools...),
harness.WithModel("claude-opus-4-6"),
harness.WithMaxSteps(10),
)
Implements the core agent loop: call the LLM → execute tool calls → feed results back → repeat. Everything else (storage, prompts, routing) is your problem.
provider/openai)provider/anthropic)memory)examples/claw provides a REPL harness for manual testingChat() providerThread type however you wantRun() from a tool for sub-agentsRun() function, not a frameworkProvider interface with one methodWithBeforeTool), streaming (WithOnDelta), and observability (WithEventHandler)WithToolFilterPendingToolCalls for approval workflowsRun, messages, tools, provider interface)StopPaused, PendingToolCalls, Thread.ResolvePending)examples/clawgo test, go test -race, and go vetprovider/openai Responses adapter (stateful continuation + streaming)provider/anthropic adapter (non-streaming + streaming)The docs/ directory is the source of truth for design and implementation guidance.
thread := harness.NewThread()
thread.AddUser("Delete old preview deployments")
result, err := harness.Run(ctx, provider,
harness.WithMessages(thread.Messages...),
harness.WithTools(tools...),
harness.WithBeforeTool(func(ctx context.Context, call harness.ToolCall) (harness.ToolAction, error) {
if call.Name == "delete_deployment" {
return harness.ToolActionPause, nil
}
return harness.ToolActionContinue, nil
}),
)
if result != nil {
thread.Append(result)
}
if err != nil {
return err
}
if result.StopReason == harness.StopPaused {
// approval flow happens outside the harness
err = thread.ResolvePending(ctx, func(ctx context.Context, call harness.ToolCall) (*harness.ToolResult, error) {
return executeApprovedTool(ctx, call)
})
if err != nil {
return err
}
result, err = harness.Run(ctx, provider,
harness.WithMessages(thread.Messages...),
harness.WithTools(tools...),
)
if result != nil {
thread.Append(result)
}
if err != nil {
return err
}
}
result, err := harness.Run(ctx, provider,
harness.WithMessages(thread.Messages...),
harness.WithTools(readTool, writeTool),
harness.WithToolFilter(func(step int, _ []harness.Message) []harness.Tool {
if step == 0 {
return []harness.Tool{readTool}
}
return []harness.Tool{readTool, writeTool}
}),
)
Use runner.Runner when you want to interrupt an in-flight run from external control input such as a user saying "stop".
r := runner.New()
done, err := r.Start(context.Background(), thread.ID, func(ctx context.Context) error {
result, err := harness.Run(ctx, provider,
harness.WithMessages(thread.Messages...),
harness.WithTools(tools...),
)
if result != nil {
thread.Append(result)
}
return err
})
if err != nil {
return err
}
// elsewhere: control-plane stop command
if strings.EqualFold(strings.TrimSpace(userInput), "stop") {
r.Stop(thread.ID)
}
runErr := <-done
_ = runErr
OPENAI_API_KEY=... go run ./examples/claw
Then type prompts or control commands (/stop, /history, /tools, /memory, /remember <text>, /new, /quit).
The example enables file-backed memory by default at ~/.agent-harness/claw;
pass --memory-dir "" to disable it.
See docs/architecture.md for the primary implementation guide.
Go
100.0%
A minimal, composable Go library for building agentic tool-calling loops
Go
5
18 commits
updated Jul 19, 2026
A minimal, composable Go library for building agentic tool-calling loops on top of LLM APIs.
1.26+result, err := harness.Run(ctx, provider,
harness.WithSystem("You are a helpful assistant."),
harness.WithMessages(thread.Messages...),
harness.WithTools(tools...),
harness.WithModel("claude-opus-4-6"),
harness.WithMaxSteps(10),
)
Implements the core agent loop: call the LLM → execute tool calls → feed results back → repeat. Everything else (storage, prompts, routing) is your problem.
provider/openai)provider/anthropic)memory)examples/claw provides a REPL harness for manual testingChat() providerThread type however you wantRun() from a tool for sub-agentsRun() function, not a frameworkProvider interface with one methodWithBeforeTool), streaming (WithOnDelta), and observability (WithEventHandler)WithToolFilterPendingToolCalls for approval workflowsRun, messages, tools, provider interface)StopPaused, PendingToolCalls, Thread.ResolvePending)examples/clawgo test, go test -race, and go vetprovider/openai Responses adapter (stateful continuation + streaming)provider/anthropic adapter (non-streaming + streaming)The docs/ directory is the source of truth for design and implementation guidance.
thread := harness.NewThread()
thread.AddUser("Delete old preview deployments")
result, err := harness.Run(ctx, provider,
harness.WithMessages(thread.Messages...),
harness.WithTools(tools...),
harness.WithBeforeTool(func(ctx context.Context, call harness.ToolCall) (harness.ToolAction, error) {
if call.Name == "delete_deployment" {
return harness.ToolActionPause, nil
}
return harness.ToolActionContinue, nil
}),
)
if result != nil {
thread.Append(result)
}
if err != nil {
return err
}
if result.StopReason == harness.StopPaused {
// approval flow happens outside the harness
err = thread.ResolvePending(ctx, func(ctx context.Context, call harness.ToolCall) (*harness.ToolResult, error) {
return executeApprovedTool(ctx, call)
})
if err != nil {
return err
}
result, err = harness.Run(ctx, provider,
harness.WithMessages(thread.Messages...),
harness.WithTools(tools...),
)
if result != nil {
thread.Append(result)
}
if err != nil {
return err
}
}
result, err := harness.Run(ctx, provider,
harness.WithMessages(thread.Messages...),
harness.WithTools(readTool, writeTool),
harness.WithToolFilter(func(step int, _ []harness.Message) []harness.Tool {
if step == 0 {
return []harness.Tool{readTool}
}
return []harness.Tool{readTool, writeTool}
}),
)
Use runner.Runner when you want to interrupt an in-flight run from external control input such as a user saying "stop".
r := runner.New()
done, err := r.Start(context.Background(), thread.ID, func(ctx context.Context) error {
result, err := harness.Run(ctx, provider,
harness.WithMessages(thread.Messages...),
harness.WithTools(tools...),
)
if result != nil {
thread.Append(result)
}
return err
})
if err != nil {
return err
}
// elsewhere: control-plane stop command
if strings.EqualFold(strings.TrimSpace(userInput), "stop") {
r.Stop(thread.ID)
}
runErr := <-done
_ = runErr
OPENAI_API_KEY=... go run ./examples/claw
Then type prompts or control commands (/stop, /history, /tools, /memory, /remember <text>, /new, /quit).
The example enables file-backed memory by default at ~/.agent-harness/claw;
pass --memory-dir "" to disable it.
See docs/architecture.md for the primary implementation guide.
Go
100.0%