builder

package
v0.2.16 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 7, 2026 License: AGPL-3.0 Imports: 44 Imported by: 0

Documentation

Overview

Package builder implements the agent build and upgrade pipeline.

Index

Constants

This section is empty.

Variables

View Source
var ErrUpgradeInProgress = errors.New("upgrade already in progress")

ErrUpgradeInProgress is returned when an upgrade is already running for the agent.

Functions

func CommitAndPush

func CommitAndPush(workDir, message string) (string, error)

CommitAndPush commits all changes on the current branch in workDir, pushes to origin. Returns commit hash.

func CommitScaffold

func CommitScaffold(repoPath, agentID string, data scaffold.ScaffoldData) (string, error)

CommitScaffold creates branch build/{agentID}/init, materializes scaffold, commits. Returns the commit hash. Safe to call on retry — deletes a stale branch if it exists.

func CreateBranch

func CreateBranch(repoPath, branch string) error

CreateBranch creates a new branch from main/master.

func CreateUpgradeBranch

func CreateUpgradeBranch(repoPath, agentID, runID string) error

CreateUpgradeBranch creates branch upgrade/{agentID}/{runID} from main. Uses `-B` (create-or-reset) so retries don't fail on a leftover branch from a prior failed attempt — Fix-this-error reuses the failed run's id as the branch suffix, and successful upgrades currently never delete the branch after merging, so collisions are easy to hit. Whatever was on the old branch was from a failed run and not worth keeping.

func InitMonorepo

func InitMonorepo(path string) error

InitMonorepo initializes a bare git repo at path if it doesn't exist.

func MergeBranch

func MergeBranch(repoPath, branch string) error

MergeBranch fast-forward merges branch into main, rebasing the branch onto current main first if a parallel build advanced main while this branch's work was running. Cross-agent rebases never conflict (each agent lives under its own agents/<id>/), and the per-agent build lock prevents same-agent races, so the rebase is safe in practice.

func RemoveAgentCode

func RemoveAgentCode(repoPath, agentID string) error

RemoveAgentCode deletes the agent directory from the monorepo and commits.

func SparseCheckout

func SparseCheckout(repoPath, branch, agentID, targetDir string) error

SparseCheckout clones the repo into targetDir with only agents/{agentID}/ checked out.

Types

type BuildInput

type BuildInput struct {
	AgentID         string
	Name            string
	Slug            string
	UserID          string
	BuildProviderID pgtype.UUID // providers row FK; pairs with BuildModel
	BuildModel      string      // bare model name; "" + invalid FK ⇄ inherit system default
	Instructions    string      // optional: when non-empty, run Sol code generation after scaffold
}

BuildInput describes what to build.

type BuildService

type BuildService struct {
	// contains filtered or unexported fields
}

BuildService orchestrates the agent build and upgrade pipeline.

func New

func New(cfg *config.Config, database *db.DB, containers container.ContainerManager, encryptor secrets.Store, logger *zap.Logger) *BuildService

New creates a BuildService. Panics if any dependency is nil.

func (*BuildService) AcquireUpgradeLock

func (b *BuildService) AcquireUpgradeLock(ctx context.Context, agentID string) error

Upgrade runs the upgrade pipeline for an existing agent. This is synchronous — caller should run in a goroutine if needed. AcquireUpgradeLock atomically checks that no upgrade is running for the agent and sets upgrade_status to "building". Returns ErrUpgradeInProgress if an upgrade is already active. Call RunUpgrade after a successful lock.

func (*BuildService) Build

func (b *BuildService) Build(_ context.Context, input BuildInput) error

Build runs the full build pipeline: scaffold → Sol code gen → compile → containerize → deploy. This is synchronous — caller should run in a goroutine if needed. If input.AgentID is set, uses the existing agent record (created by the API handler for async 202 responses). If empty, creates a new agent record.

func (*BuildService) CancelBuild

func (b *BuildService) CancelBuild(agentID string) bool

CancelBuild cancels a running build/upgrade for the given agent. Returns true if a build was running and cancelled. Does not block on teardown — use CancelBuildAndWait when the caller needs the toolserver and DB writes to settle before proceeding.

func (*BuildService) CancelBuildAndWait added in v0.2.8

func (b *BuildService) CancelBuildAndWait(agentID string, timeout time.Duration) bool

CancelBuildAndWait cancels a running build/upgrade and blocks until the goroutine has run its deferred cleanup (toolserver SIGKILL, DB status write) or until timeout elapses. Returns true if a build was running. Used by Delete to avoid racing the workspace rm and agent-row delete against the upgrade's in-flight writes.

func (*BuildService) MonorepoPath

func (b *BuildService) MonorepoPath() string

MonorepoPath returns the agent monorepo path.

func (*BuildService) RecoverStuckOperations

func (b *BuildService) RecoverStuckOperations(ctx context.Context) error

RecoverStuckOperations resets any builds or upgrades left in progress after an unclean shutdown. Should be called on startup.

func (*BuildService) RunUpgrade

func (b *BuildService) RunUpgrade(_ context.Context, input UpgradeInput)

RunUpgrade executes the upgrade pipeline for an agent that already has its upgrade_status set to "building" via AcquireUpgradeLock.

func (*BuildService) SetEventPublisher

func (b *BuildService) SetEventPublisher(ep EventPublisher)

SetEventPublisher sets the event publisher for build/upgrade lifecycle events.

func (*BuildService) SetUpgradeNotifier

func (b *BuildService) SetUpgradeNotifier(n PostUpgradeNotifier)

SetUpgradeNotifier sets the notifier called after successful upgrades to notify the originating conversation.

func (*BuildService) WarmBuildCache

func (b *BuildService) WarmBuildCache(ctx context.Context)

WarmBuildCache pre-downloads Go module dependencies so that the first real agent build hits a warm cache. Materializes a real scaffold and runs a full docker build (same Dockerfile.tmpl + go.mod.tmpl as real builds), then removes the throwaway image. The cache mounts persist.

func (*BuildService) WarmRuntimeCaches

func (b *BuildService) WarmRuntimeCaches(ctx context.Context)

WarmRuntimeCaches seeds the named volumes that the build-prompt loop's direct `go mod tidy` / `go build` invocations consume — distinct from the BuildKit cache mount that WarmBuildCache populates. The agent-builder container at runtime sets GOMODCACHE=/tmp/go-mod and GOCACHE=/tmp/go-cache (see container/docker.go) backed by the airlock-go-mod-cache and airlock-go-build-cache Docker named volumes. Without this seed, the first build-prompt iteration pays full download cost for ~25 modules in agentsdk+sol's transitive dep tree. The volumes persist across airlock restarts.

type BuilderPromptData

type BuilderPromptData struct {
	HasWebSearch bool
}

BuilderPromptData holds template data for the builder system prompt.

type EventPublisher

type EventPublisher interface {
	PublishBuildEvent(ctx context.Context, agentID, buildID uuid.UUID, status, errMsg string)
	PublishBuildLogLine(ctx context.Context, agentID, buildID uuid.UUID, seq int64, stream, line string)
}

EventPublisher publishes build/upgrade lifecycle events. Implemented by realtime.PubSub via an adapter.

type LibsPaths

type LibsPaths struct {
	// Owned is the directory containing agentsdk/goai/sol. Either the
	// operator's AGENT_LIBS_PATH (live dev source) or the extracted cache.
	Owned string
	// Ext is the directory containing goose/templ. Always the extracted
	// cache (devs don't edit these).
	Ext string
}

LibsPaths is what callers need to wire up the per-agent docker build's two BuildKit contexts (libs-owned + libs-ext) and the toolserver's bind-mount overlay (Owned).

func EnsureLibs

func EnsureLibs(ctx context.Context, image, explicitPath, cacheDir string, logger *zap.Logger) (LibsPaths, error)

EnsureLibs guarantees that:

  • The agent-builder image's full /libs/* set is materialized on the host under <cacheDir>/<image-id>/, cached by image ID so a tag bump triggers re-extraction.
  • If explicitPath (AGENT_LIBS_PATH) is set, the owned subdirs all exist there. Owned then points at explicitPath; Ext at the cache.
  • If explicitPath is empty (prod), both Owned and Ext point at the cache.

type PostUpgradeNotifier

type PostUpgradeNotifier interface {
	NotifyUpgradeComplete(ctx context.Context, agentID uuid.UUID, conversationID, status, message string) error
}

PostUpgradeNotifier is called after an upgrade finishes (success or failure) to post a single message into the originating conversation.

status is "success" or "error". message is the human-readable text to surface — typically sourced from the agent-builder's exit tool on success, or the underlying failure reason on error. The notifier posts exactly ONE message and does not trigger a follow-up LLM turn — the agent's own exit-tool summary already describes the outcome, so re-prompting just produces redundant text.

type UpgradeInput

type UpgradeInput struct {
	AgentID        string
	RunID          string // the run that triggered the upgrade
	Reason         string // "llm_request", "auto_fix", "manual"
	Description    string // what to change
	ConversationID string // conversation that triggered the upgrade (for post-upgrade reply)
	ErrorMessage   string // from failed run (auto_fix)
	PanicTrace     string // from failed run (auto_fix)
	InputPayload   string // JSON of failed run input (auto_fix)
	Actions        string // JSON of recorded actions before failure (auto_fix)
	Messages       string // conversation messages from the failed run
}

UpgradeInput describes an upgrade request.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL