Your First Agent
Build a Gibson agent with the SDK, the Agent contract, the Harness, and a working hello-world example.
An agent is the autonomous reasoning layer of a Gibson mission. It decides what to do next, calls tools, queries the knowledge graph, and submits findings. Gibson runs your agent inside a mission alongside any other agents, tools, and plugins the mission references.
This page shows how to write an agent from scratch with the Gibson SDK.
Prerequisites
- You completed Install, and
gibson inspectreturns successfully for a registered agent identity. - Go 1.26+ (the ADK pins the exact version in its
gibson/go.mod).
If you have not enrolled an agent yet, do that first.
gibson agent enroll --name <name> --kind agent mints a bootstrap token.
gibson component register --token <T> exchanges the token for the
runtime credential the SDK needs.
What an agent looks like
In the SDK, an agent is anything that satisfies the agent.Agent
contract from github.com/zeroroot-ai/sdk/agent. Do not write the
struct by hand. Use the builder:
cfg := agent.NewConfig().
SetName("my-agent").
SetVersion("0.1.0").
SetDescription("…").
AddCapability("recon").
AddTargetType("web_app").
AddTechniqueType("input_fuzzing").
AddLLMSlot("primary", llm.SlotRequirements{}).
SetExecuteFunc(execute)
a, err := agent.New(cfg)
What the fields mean:
| Field | Purpose |
|---|---|
Name | Unique within your tenant. Mission nodes refer to your agent by name. |
Version | Semantic version. Useful for canarying upgrades. |
Description | Human-readable purpose. Shown in the dashboard. |
Capabilities | Free-form labels ("recon", "prompt_injection", …). Used for routing and discovery. |
TargetTypes | The shape of system this agent works against ("web_app", "k8s_cluster", "llm_chat"). |
TechniqueTypes | Attack techniques employed. |
LLMSlots | Slot identifiers + requirements. The mission binds each slot to a real model at dispatch time. |
SetExecuteFunc | The actual work. See below. |
The Harness
Your agent never talks to Gibson directly. It calls agent.Harness,
which is the view of the platform for the agent. Gibson passes the
harness into Execute. The harness exposes everything an agent might
need:
| You want to… | Use |
|---|---|
| Call an LLM | h.Complete, h.CompleteWithTools, h.Stream, h.CompleteStructured |
| Call a tool | h.CallToolProto, h.CallToolProtoStream, h.QueueToolWork, h.ToolResults |
| Query a plugin | h.QueryPlugin, h.ListPlugins |
| Hand a subtask to another agent | h.DelegateToAgent, h.ListAgents |
| Persist a security finding | h.SubmitFinding, h.GetFindings |
| Read the knowledge graph | h.QueryNodes, h.FindSimilarAttacks, h.FindSimilarFindings, h.GetAttackChains |
| Write to the knowledge graph | h.StoreNode |
| Use working / mission / long-term memory | h.Memory() |
| See the mission's target and metadata | h.Mission(), h.Target() |
| Spawn a sub-mission | h.CreateMission, h.RunMission |
| Read or write a workspace file | h.Workspace(), h.Workspaces() |
| Extra authz check before a sensitive op | h.Authorize(ctx, action, resource) |
The agent defines its LLM slot identifiers ("primary", "summarizer", …).
The slot policy of your tenant maps each one to a real provider and
model at dispatch time. See Observability for
cost and latency metrics.
A complete hello-world
package main
import (
"context"
"log"
"github.com/zeroroot-ai/sdk/agent"
"github.com/zeroroot-ai/sdk/llm"
)
func execute(ctx context.Context, h agent.Harness, t agent.Task) (agent.Result, error) {
resp, err := h.Complete(ctx, "primary", []llm.Message{
{Role: llm.RoleSystem, Content: "You greet people warmly in one sentence."},
{Role: llm.RoleUser, Content: t.Prompt},
})
if err != nil {
return agent.NewErrorResult(err), nil
}
return agent.NewSuccessResult(resp.Content), nil
}
func main() {
cfg := agent.NewConfig().
SetName("hello-agent").
SetVersion("0.1.0").
SetDescription("Says hi via the primary LLM slot.").
AddCapability("greeting").
AddTargetType("none").
AddLLMSlot("primary", llm.SlotRequirements{}).
SetExecuteFunc(execute)
a, err := agent.New(cfg)
if err != nil {
log.Fatal(err)
}
if err := agent.Serve(context.Background(), a); err != nil {
log.Fatal(err)
}
}
agent.Serve reads ~/.gibson/agent/credentials, connects to your
tenant, registers the agent, and waits for missions to dispatch work.
Run the agent
go run .
Leave the process running. In the Missions section of the dashboard, add a node that points at the name of your agent. Click Run. The mission detail page streams events as your agent works. See Missions for the full event taxonomy.
A more realistic example
This bug-bounty triage agent uses a tool and the graph, and submits a finding:
func execute(ctx context.Context, h agent.Harness, t agent.Task) (agent.Result, error) {
target := h.Target()
// 1. Run a port scan tool we already enrolled.
scan, err := h.CallToolProto(ctx, "port-scan", &portscanv1.Request{
Host: target.Address,
})
if err != nil {
return agent.NewErrorResult(err), nil
}
// 2. Pull similar prior findings from the knowledge graph for context.
similar, err := h.FindSimilarFindings(ctx, agent.SimilarityRequest{
Description: "exposed admin endpoint",
Limit: 5,
})
if err != nil {
return agent.NewErrorResult(err), nil
}
// 3. Have the LLM decide what's worth flagging.
plan, err := h.CompleteStructured(ctx, "primary", planSchema, llm.Messages{
{Role: llm.RoleSystem, Content: "Triage scan output. Decide what to flag."},
{Role: llm.RoleUser, Content: render(scan, similar)},
})
if err != nil {
return agent.NewErrorResult(err), nil
}
// 4. Submit findings.
for _, item := range plan.Flag {
if err := h.SubmitFinding(ctx, agent.Finding{
Title: item.Title,
Severity: item.Severity,
TargetRef: target.Id,
Description: item.Description,
Evidence: item.Evidence,
}); err != nil {
return agent.NewErrorResult(err), nil
}
}
return agent.NewSuccessResult("triage complete"), nil
}
Their own pages explain tools, plugins, and the graph: Tools, Plugins, Knowledge graph.
Lifecycle hooks
Beyond Execute, the SDK supports optional lifecycle hooks:
Initialize(ctx, cfg): runs once when the agent process starts.Shutdown(ctx): runs on graceful shutdown.Health(ctx): Gibson polls this hook to confirm the agent is still alive.
The builder accepts these hooks through SetInitializeFunc,
SetShutdownFunc, SetHealthFunc. The defaults are no-ops and a
Healthy status. Most agents do not need to override them.
Next steps
- Tools: define and call a tool from your agent.
- Findings: what
SubmitFindingdoes and how findings appear in the dashboard. - Knowledge graph: query and write the graph from inside an agent.
- Missions: chain your agent with other components in a DAG.
- Taxonomy and Ontology: extend the type system with custom node types or industry-vocab mappings for your component.
Getting Started
Start your first Gibson agent in under an hour. Sign up, install the gibson CLI, sign in with the device flow, enroll, build, and run a mission.
Component bootstrap & auth
How agents, tools, and plugins authenticate against Gibson, the one enrollment path, the per-call protocol, and where to look when a 401 lands.