Files
probo/contrib/claude/agent.md
Bryan Frimin 3dfc833671 Replace supervisor with agentrun worker service
Move agent-run orchestration from the legacy supervisor path into the new
agentrun worker/service package and wire it through coredata, server,
policies, and GraphQL resolvers.

This consolidates run lifecycle handling around lease-aware workers and
aligns API surface with the new agent-run domain model so reviewers can
follow one coherent execution path.

Signed-off-by: Bryan Frimin <bryan@probo.com>
2026-06-08 15:27:50 +02:00

3.6 KiB

Agent (pkg/agent)

LLM agent orchestration framework.

Agent construction

agent := agent.NewAgent(
    "agent-name",
    "System instructions here",
    agent.WithTools(tool1, tool2),
    agent.WithHandoffs(otherAgent),
    agent.WithModel(model),
)

Functional options: WithTools, WithHandoffs, WithInstructions, WithModel, WithModelSettings, WithMCPServers, WithInputGuardrails, WithOutputGuardrails, WithApproval, WithSession.

Execution

result, err := agent.Run(ctx, messages)
result.FinalMessage().Text()  // final output
result.LastAgent              // agent that produced the result

Prefer RunTyped[T] over Run + manual unmarshalling when the agent declares a structured output type via WithOutputType. RunTyped validates the response against the JSON Schema and returns the typed value directly:

result, err := agent.RunTyped[TrackerIdentification](ctx, ag, messages)
identification := result.Output  // already typed, no json.Unmarshal needed

Only fall back to agent.Run when the agent produces free-form text with no output schema.

Tool interface

type Tool interface {
    Name() string
    Description() string
    Parameters() jsonschema.Schema
    Execute(ctx context.Context, input json.RawMessage) (string, error)
}

Agent-as-tool

agent.AsTool(name, description) wraps an agent as a tool for composition.

Cancellation semantics

ctx.Done() is a graceful-suspend signal, not a hard abort. When the caller cancels ctx, Run/RunStreamed/Resume/Restore let the in-flight LLM call and tool finish, persist a checkpoint via the configured Checkpointer, and return *SuspendedError. The framework shields downstream calls (LLM, tools, hooks, guardrails, save) from the cancellation so they complete naturally; the cancel is only observed at the next safe boundary.

Use agent.ErrSuspendForCheckpoint as the cancel cause when the intent is graceful suspend — workers that distinguish a graceful-stop request from infrastructure-level causes (lease loss, heartbeat failure) inspect context.Cause(ctx) to dispatch.

Implications:

  • A context.WithTimeout becomes a "max wall-clock budget then suspend" — strictly better than the alternative where the deadline kills work outright.
  • There is no in-process hard-abort path. Callers that genuinely need to kill a run terminate the process; stale recovery handles the row.
  • Tools receive a non-cancellable ctx; if a tool needs a hard deadline it must derive its own with context.WithTimeout(ctx, ...) inside the tool body.

The agent run worker (pkg/agentrun/handler.go) maps a SIGTERM-driven shutdown broadcast onto a per-run cancelRun(agent.ErrSuspendForCheckpoint), so the same contract drives both the public Go API and the worker infrastructure path.

Prompt templates

Prompt files with placeholders use .txt.tmpl (see general template naming convention in .cursor/rules/template-files.mdc).

Use agent.WithInstructionsFunc to build prompts dynamically at runtime:

//go:embed prompts/tracker_identification.txt.tmpl
var trackerIdentificationPrompt string

func trackerMappingInstructions(_ context.Context, _ *agent.Agent) string {
    categories := coredata.ThirdPartyCategories()
    parts := make([]string, len(categories))
    for i, c := range categories {
        parts[i] = string(c)
    }
    return strings.Replace(trackerIdentificationPrompt, "{{.Categories}}", strings.Join(parts, ", "), 1)
}

Limits

  • Max turns: 10 (default)
  • Max tool depth: 16 (default)
  • Depth tracking prevents infinite recursion in handoffs