Documentation
¶
Overview ¶
Package bonnie is the entry point of an authored agent tree.
A BONNIE agent is a directory of files whose meaning comes from their paths, and one Go file that starts it:
package main
import "github.com/mark3labs/bonnie"
func main() { bonnie.New().Serve() }
That is the whole default agent. Every slot in the tree has a framework default, and authoring the slot replaces it: instructions.md is the system prompt, workspace/ is the directory the agent's files live in, tools/ holds one directory per tool, and .bonnie holds the journal. Configuration that is not a file is code — an Option on New:
func main() {
bonnie.New(
bonnie.WithModel("anthropic/claude-sonnet-4-5"),
bonnie.WithSandbox(sandbox.Docker()),
).Serve()
}
There is no manifest file. Data-shaped settings live at their default paths, and everything else is a Go call, so a setting that does not exist is a compile error rather than a key that is accepted and ignored.
The tree's code — its tools — and the copies of its data files that a `bonnie build` binary carries are wired by codegen into bonnie_gen.go, which calls Register from its init. main.go never has to name them.
Index ¶
- Constants
- func Register(t Tree)
- type Agent
- type Channel
- type ChannelFunc
- type Option
- func Quiet() Option
- func WithAddr(addr string) Option
- func WithAgentFactory(f runtime.AgentFactory) Option
- func WithChannel(f ChannelFunc) Option
- func WithDiscord(cfg discord.Config) Option
- func WithInstructions(path string) Option
- func WithJournal(dir string) Option
- func WithKit(opts ...kit.Option) Option
- func WithListener(ln net.Listener) Option
- func WithModel(model string) Option
- func WithNetwork(p sandbox.NetworkPolicy) Option
- func WithSandbox(p sandbox.Provider) Option
- func WithShutdownTimeout(d time.Duration) Option
- func WithSlack(cfg slack.Config) Option
- func WithSystemPrompt(prompt string) Option
- func WithTelegram(cfg telegram.Config) Option
- func WithTools(tools ...kit.Tool) Option
- func WithWorkspace(dir string) Option
- type Tree
Constants ¶
const ( // DefaultInstructions is the system prompt file, relative to the tree. DefaultInstructions = "instructions.md" // DefaultWorkspace is the directory the agent's files live in: the // working directory of the host's file tools without a sandbox, and the // seed mirrored into the sandbox with one. DefaultWorkspace = "workspace" // DefaultSkills is the tree's skills directory. DefaultSkills = "skills" // DefaultJournal is the directory the run journal is written to. DefaultJournal = ".bonnie" // DefaultAddr is the address the HTTP channel binds when none is given. DefaultAddr = ":8080" )
The default layout of a scaffolded tree. These are the paths `bonnie init` writes, the paths codegen embeds, and the paths Agent.Run reads when no option overrides them. They are constants rather than five copies of a string literal, because the scaffold, the generator, the dev loop, and the runtime must agree on the answer: a rule written more than once is a rule one caller can honour while another misses it.
Variables ¶
This section is empty.
Functions ¶
Types ¶
type Agent ¶
type Agent struct {
// contains filtered or unexported fields
}
Agent is a configured agent: the tree's defaults with the options applied over them. Build one with New, then Agent.Serve it.
Nothing is opened, bound, or read until it serves, so building an agent cannot fail and New returns no error. A setting that cannot apply — a network policy with no sandbox to enforce it, a model beside a host-supplied agent factory — is refused when serving starts, which is the first moment the whole configuration is known.
func New ¶
New builds an agent from the tree's default layout and the options.
func main() { bonnie.New().Serve() }
With no options that is a complete agent: instructions.md is the system prompt, workspace/ is the agent's root for files, .bonnie is the journal, the tools under tools/ are wired by codegen, and the HTTP channel is served on :8080. Each Option replaces one of those.
func (*Agent) Run ¶
Run serves the agent until ctx ends, then drains in-flight turns and returns. It is Agent.Serve without the process: no flags, no signal handler, no exit — for a host that already owns those.
A run that parks holds no compute and lives in the journal, so stopping here is never destructive.
func (*Agent) Serve ¶
func (a *Agent) Serve()
Serve runs the agent until the process is interrupted, then exits.
It owns the process, which is what makes a one-line main possible: it parses the operator flags a serving binary accepts, installs the signal handler, drains in-flight turns on SIGINT or SIGTERM, and exits non-zero after writing the error to stderr. A host that owns its own process calls Agent.Run instead.
The flags are -addr and -model, and each wins over the matching option, so an operator can move a built binary to another port or model without rebuilding it. `bonnie dev` starts a tree's binary with -addr, which is the whole contract between the dev loop and the child.
type Channel ¶
Channel is an inbound transport BONNIE can mount: the HTTP routes it needs and the inbound surface a run resolves through. Every adapter in channel/ is both.
type ChannelFunc ¶
ChannelFunc builds a channel for the runner that serves it. It is called once at start; an error stops the process before it listens.
type Option ¶
type Option func(*config)
Option configures New. This is where a setting that is not a file in the tree lives: the model, a sandbox, an extra channel. A setting that does not exist is a compile error, which is the point.
func Quiet ¶
func Quiet() Option
Quiet suppresses the startup banner. The no-sandbox warning is printed anyway: what runs unisolated must never be quieter than what does not.
func WithAddr ¶
WithAddr binds the HTTP channel to addr instead of DefaultAddr.
func WithAgentFactory ¶
func WithAgentFactory(f runtime.AgentFactory) Option
WithAgentFactory replaces the model-backed agent entirely with one the host builds itself. It is the escape hatch for a program that implements runtime.Agent — a test double, a router, a second framework — and wants BONNIE only for durability and transport.
It cannot be combined with the options that configure the agent BONNIE would have built (WithModel, WithSystemPrompt, WithSandbox, WithNetwork, WithTools, WithKit): the factory owns the agent, so those settings would be accepted and ignored. Agent.Run refuses instead, naming both.
What the factory owns, it owns completely: the tree's instructions and the tools codegen discovered do not reach it either. They are available through Registered for a host that wants them. The journal, the workspace, the channels, and the shutdown behaviour are unaffected — those are BONNIE's side of the boundary.
func WithChannel ¶
func WithChannel(f ChannelFunc) Option
WithChannel mounts another inbound transport beside the HTTP channel.
func WithDiscord ¶
WithDiscord mounts the Discord channel. DISCORD_BOT_TOKEN and DISCORD_PUBLIC_KEY come from the environment; see WithSlack.
func WithInstructions ¶
WithInstructions reads the system prompt from path instead of DefaultInstructions. An empty path means the agent has no instructions file, which is how a host with no tree runs.
func WithJournal ¶
WithJournal writes the run journal to dir instead of DefaultJournal.
func WithKit ¶
WithKit passes Kit options through to the agent, for settings BONNIE does not name itself.
func WithListener ¶
WithListener serves on an already-bound listener instead of dialling the configured address. Tests bind :0 with it and learn the port.
func WithModel ¶
WithModel selects the model, as "provider/name". Without it, Kit's default applies.
func WithNetwork ¶
func WithNetwork(p sandbox.NetworkPolicy) Option
WithNetwork constrains what the sandbox may reach. It needs a sandbox: a policy with nothing to enforce it is refused at startup, never stored and ignored.
func WithSandbox ¶
WithSandbox runs every tool call in p instead of in this process.
Without it, a model-chosen tool call has this process's files, network, and credentials, and Agent.Run says so at startup. See docs/SANDBOX.md.
func WithShutdownTimeout ¶
WithShutdownTimeout is how long Agent.Run waits for in-flight turns to reach a checkpoint after a signal. A turn that is cut short still keeps its finished steps — the journal is what survives — but a clean stop is cheaper.
func WithSlack ¶
WithSlack mounts the Slack channel. Credentials come from the environment and never from code: SLACK_BOT_TOKEN and SLACK_SIGNING_SECRET fill the config's empty fields, and a missing one is a startup error naming the variable. A webhook that does not verify its caller is a door with no lock.
func WithSystemPrompt ¶
WithSystemPrompt sets the system prompt directly, instead of reading the tree's instructions file. It wins over WithInstructions.
func WithTelegram ¶
WithTelegram mounts the Telegram channel. TELEGRAM_BOT_TOKEN and TELEGRAM_WEBHOOK_SECRET come from the environment; see WithSlack.
func WithTools ¶
WithTools adds tools to the set the model may call, beside the tools codegen discovered under tools/ and Kit's core set.
func WithWorkspace ¶
WithWorkspace roots the agent's files at dir instead of DefaultWorkspace. An empty dir means no workspace: the process's own directory stays the root, which is how a host with no tree runs.
The workspace is what keeps a model's write off the tree itself — the instructions, the journal, and the source beside them.
type Tree ¶
type Tree struct {
// Tools are the tools discovered under tools/, one per directory.
Tools []kit.Tool
// Instructions is the embedded copy of the tree's instructions file.
Instructions string
// Skills is the embedded copy of the tree's skills directory.
//
// Reserved: it is embedded so a later skill loader has it, and nothing
// reads it today. It is stated here rather than implied, because a field
// that quietly does nothing is the failure this package exists to avoid.
Skills embed.FS
// Workspace is the embedded copy of the tree's workspace seed files.
// [Agent.Run] materialises them beside a built binary that has no tree, and
// never overwrites a file that is already there.
Workspace embed.FS
}
Tree is what codegen discovered in an agent tree: the code it wired and the data files it embedded. The generated bonnie_gen.go builds one and hands it to Register from its init, so main.go never names a tool or an embed.
A tree run from its source directory reads its data files from disk and uses the embedded copies only as a fallback. A binary from `bonnie build` has no tree beside it, so the embedded copies are all it has.
func Registered ¶
func Registered() Tree
Registered returns what the generated file registered. It is the zero Tree when a tree has not been generated yet, which is what a hand-written main.go with no tools sees.
Call it at run time — inside main, or later. The generated file registers from init, and Go initialises every package-level variable before it runs any init function, so a package-level `var x = bonnie.Registered()` reads the empty tree.
Directories
¶
| Path | Synopsis |
|---|---|
|
Package agent implements BONNIE's L2 discovery: the authored agent tree.
|
Package agent implements BONNIE's L2 discovery: the authored agent tree. |
|
Package channel defines BONNIE's inbound transport abstraction (L3).
|
Package channel defines BONNIE's inbound transport abstraction (L3). |
|
chat
Package chat holds the plumbing every chat-platform channel shares: the journalled address map, per-run turn locks, the channel.SessionRef implementation, and the dispatch rule that sends a message to the right runner entry point.
|
Package chat holds the plumbing every chat-platform channel shares: the journalled address map, per-run turn locks, the channel.SessionRef implementation, and the dispatch rule that sends a message to the right runner entry point. |
|
discord
Package discord is BONNIE's Discord inbound transport (L3).
|
Package discord is BONNIE's Discord inbound transport (L3). |
|
http
Package http is BONNIE's HTTP inbound transport (L3).
|
Package http is BONNIE's HTTP inbound transport (L3). |
|
slack
Package slack is BONNIE's Slack inbound transport (L3).
|
Package slack is BONNIE's Slack inbound transport (L3). |
|
telegram
Package telegram is BONNIE's Telegram inbound transport (L3).
|
Package telegram is BONNIE's Telegram inbound transport (L3). |
|
Package channeltest is the conformance suite for channel adapters, in the same spirit as the journal and sandbox suites.
|
Package channeltest is the conformance suite for channel adapters, in the same spirit as the journal and sandbox suites. |
|
cmd
|
|
|
bonnie
command
Command bonnie is the BONNIE developer CLI.
|
Command bonnie is the BONNIE developer CLI. |
|
bonnie/tui
Package tui is the BONNIE terminal user interface.
|
Package tui is the BONNIE terminal user interface. |
|
examples
|
|
|
hitl-restart
command
Command hitl-restart is the headline demonstration: a run parks for a human, the process exits, and a completely new process finishes the run.
|
Command hitl-restart is the headline demonstration: a run parks for a human, the process exits, and a completely new process finishes the run. |
|
minimal
command
Command minimal starts one durable run and prints the answer.
|
Command minimal starts one durable run and prints the answer. |
|
internal
|
|
|
treetest
Package treetest builds throwaway agent trees that compile against this checkout, for tests that need a real `go build` of a scaffolded module.
|
Package treetest builds throwaway agent trees that compile against this checkout, for tests that need a real `go build` of a scaffolded module. |
|
Package runtime is BONNIE's durable execution layer (L1).
|
Package runtime is BONNIE's durable execution layer (L1). |
|
Package sandbox gives a BONNIE run an isolated place to run tool calls.
|
Package sandbox gives a BONNIE run an isolated place to run tool calls. |