Skip to content

The layers

harness — Compose a Product

Compile declarative artifacts — agent definitions, skills, instructions — into a configured durable agent

Read this when
  • Building an agent product with skills and instruction documents
  • Turning static definitions into system prompts and middleware
  • Understanding why New returns a factory, not an agent

You can compose an agent product from definitions, skills, and instructions with pkg/harness.

pkg/harnesspkg/agentpkg/durable · session
fig. 01 — What one Harness.Agent build compiles

Resolvers supply the artifacts. The harness compiles them into a system prompt, first-run seed entries, a skill tool, and middleware. Then it returns a configured durable.Agent and gets out of the way.

proj := os.DirFS(workdir)
h, err := harness.New(
harness.WithCatalog(cat),
harness.WithDefaultModel("anthropic-messages/claude-sonnet-4-6"),
harness.WithWorkDir(workdir),
harness.WithSkills(
def.Skills(def.Skill{Name: "summarize" /* ... */}), // declared in code
fs.Skills(proj, ".agents/skills"), // the project wins
),
harness.WithInstructions(fs.Instructions(proj, "AGENTS.md")),
// ... WithAgents, WithTools
)
a, err := h.Agent(ctx, durable.WithSessionID("ticket-8472"))
msgs, err := a.Run(ctx, durable.Text("Commit the change in web/.")).Wait()

New returns a factory

One harness serves many sessions and many working directories. It holds baseline configuration, and each build overlays the options of one conversation.

// Process-wide baseline: the skills of the user.
h, err := harness.New(
harness.WithSkills(fs.Skills(os.DirFS(userConfigDir()), "skills")),
// ...
)
// One session, in one repository, layered on top.
a, err := h.Agent(ctx,
durable.WithSessionID("ticket-8472"),
harness.WithWorkDir(repo),
harness.WithSkills(fs.Skills(os.DirFS(repo), ".agents/skills")),
)

A built agent keeps its snapshot for its lifetime. The next build reads the current state of every source.

The harness owns the prompt

h, err := harness.New(
harness.WithPromptBuilder(myBuilder), // replace prompt construction
harness.WithSeed(prompt.NoSeed), // start a fresh session empty
)

harness.New rejects agent.WithSystemPrompt, because prompt construction is the job of this layer. On the first run of a fresh session, the harness prepends the seed entries. A resumed session skips them, because history already holds them.

The seeder marks its own entries, and the harness passes the choice through. prompt.DefaultSeed marks its environment block ai.InjectionMeta: the model reads it, the transcript hides it, and the store keeps it, so it comes back on resume. That is the usual choice, not an enforced one — a seeder that wants an opening message the reader can see leaves the entry unmarked, and one that seeds from live state marks it ephemeral. See Messages.

Go deeper