agent

package
v0.1.3 Latest Latest
Warning

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

Go to latest
Published: Jul 31, 2026 License: Apache-2.0 Imports: 22 Imported by: 0

Documentation

Overview

Package agent embodies the Loop as a compact ReAct cycle over the fixed M1 tool belt (edit / read_file / ls / exec / run_pov), talking to a model through the pluggable model seam and choosing that model through the Router seam. This is the "inner tool-belt loop" M1 exists to prove: a solid editor, an exec with output caps, and a tight observe-error-retry cycle, all disciplined by the oracle — the model proposes, only run_pov disposes.

The driver is intentionally framework-free so it is fully testable offline; the same seam accepts an Eino-backed model/loop later with no call-site changes.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ToolDefs

func ToolDefs(tools []Tool) []model.ToolDef

ToolDefs converts a belt into model tool definitions.

Types

type CompactInput

type CompactInput struct {
	System     model.Message   // role-scoped system prompt (kept verbatim)
	Objective  string          // kept verbatim
	Messages   []model.Message // current, over-budget working history
	KeepRecent int             // recent turns to preserve verbatim (~6; default 6)

	Facts        []store.Entry      // oracle-verified spine
	Observations []store.Entry      // gathered, not certified (run_pov verdicts, tool findings)
	ActiveHyps   []store.Hypothesis // the open frontier
	RuledOut     []store.Hypothesis // resolved lines — the anti-drift memory
}

CompactInput is the durable state the projection is built from, passed explicitly so a compactor is testable without a store.

type Compactor

type Compactor interface {
	Compact(ctx context.Context, in CompactInput) ([]model.Message, error)
}

Compactor reprojects a run's working context onto a bounded view over the durable trajectory. Called when the estimated prompt exceeds ContextBudget.

type Config

type Config struct {
	Role        router.Role
	TaskKind    router.TaskKind
	Objective   string
	TargetDesc  string
	MaxIters    int
	Temperature float64
	MaxTokens   int

	// M3 governance: guards for unattended, drift-resistant long runs.
	// TokenBudget halts the run once cumulative tokens reach it (0 → unlimited).
	// StallLimit halts after this many consecutive iterations with no new
	// observation and no new PoV submission — the loop is spinning (0 → default 5).
	TokenBudget int
	StallLimit  int

	// Context compaction (context compaction): when the estimated prompt
	// exceeds ContextBudget tokens, the working context is reprojected onto a
	// bounded digest over the durable trajectory (needs ReAct.Compactor set).
	// 0 → disabled. KeepRecent is the number of recent turns kept verbatim (default 6).
	ContextBudget int
	KeepRecent    int
}

Config parameterizes a run.

type ContainerSandbox

type ContainerSandbox struct {
	DockerBin string        // default "docker"
	Image     string        // MUST be digest-pinned (contains "@sha256:")
	HostDir   string        // workspace root, bind-mounted at /work
	Name      string        // container name
	Memory    string        // e.g. "2g" (default)
	PidsLimit int           // default 512
	StartWait time.Duration // grace for the keepalive to come up (default 20s)
	// Network is the docker network mode. Default "none" (fully air-gapped). A
	// named network is the SCOPED HOLE: the operator provisions a
	// network where only the tool broker/store is reachable — pull-only tool
	// provisioning, never open egress the untrusted target could exfil through.
	Network string
	// contains filtered or unexported fields
}

ContainerSandbox isolates the agent's exec in a long-lived, locked-down container: the workspace is bind-mounted at /work (so the host-side file tools and in-container exec see the same files), and the container has no network, no capabilities, no privilege escalation, a read-only root, and — critically — NO docker socket, so a hostile target cannot pivot to the host or its Docker. The image is pinned by digest so the sandbox is reproducible and cannot drift.

func NewContainerSandbox

func NewContainerSandbox(dockerBin, image, hostDir, name string) (*ContainerSandbox, error)

NewContainerSandbox validates the image is digest-pinned and returns a sandbox bound to hostDir. The container is created lazily on the first Exec.

func (*ContainerSandbox) Close

func (s *ContainerSandbox) Close() error

Close removes the container. Safe to call if it never started.

func (*ContainerSandbox) Exec

func (s *ContainerSandbox) Exec(ctx context.Context, name string, args []string, stdin string, timeout time.Duration, maxOutput int) (ExecResult, error)

Exec runs one command via `docker exec` in the keepalive container. Semantics match the host Workspace.Exec (no shell; args passed verbatim), so the tool contract is unchanged — only the execution boundary moves.

type ExecResult

type ExecResult struct {
	ExitCode int
	Stdout   string
	Stderr   string
	TimedOut bool
}

ExecResult is the outcome of one command.

type ModelCompactor

type ModelCompactor struct {
	Model  model.Model
	Router router.Router
}

ModelCompactor augments the deterministic template digest with a cheap-tier model summary of the turns being compacted away — the free-text reasoning that no structured entry captures (context compaction: the second of the two implementations). It is LOSSY by design and that is safe: the template digest (durable facts/observations/ruled-out) remains authoritative, so any model failure falls back to template-only. The summary call routes cheap (RoleCompactor / Checkable) — a compaction step must not cost a strong-model call.

func (ModelCompactor) Compact

func (c ModelCompactor) Compact(ctx context.Context, in CompactInput) ([]model.Message, error)

type Outcome

type Outcome struct {
	Confirmed    bool
	Iterations   int
	StopReason   string // confirmed | model-concluded | max-iters | error
	TotalUsage   model.Usage
	FinalMessage string
}

Outcome summarizes how the agent loop ended.

type ReAct

type ReAct struct {
	Model   model.Model
	Router  router.Router
	Session *Session
	Tools   []Tool
	// Compactor, if set, reprojects the working context onto a bounded view over
	// the durable trajectory when the prompt approaches ContextBudget — so a run
	// goes long instead of halting when history fills (context compaction).
	// nil → no compaction (the M1 behavior).
	Compactor Compactor
	// Log, if set, receives short human-readable progress lines.
	Log func(string)
}

ReAct drives one agent over a Session.

func (*ReAct) Run

func (r *ReAct) Run(ctx context.Context, cfg Config) (Outcome, error)

Run executes the ReAct loop until the oracle confirms, the model concludes, the iteration budget is spent, or the context is cancelled.

type Sandbox

type Sandbox interface {
	Exec(ctx context.Context, name string, args []string, stdin string, timeout time.Duration, maxOutput int) (ExecResult, error)
	Close() error
}

Sandbox runs the agent's exec somewhere other than the host. Implementations MUST NOT expose the host filesystem (beyond the workspace), the docker socket, or — by default — the network. It is the M2 security fix for the "exec runs on the host" hole: the agent's shell is the one surface an attacker-controlled target could try to pivot through, so it is isolated.

type Session

type Session struct {
	Store    *store.Store
	Verifier *verify.Verifier

	RunID        string
	HypothesisID string
	Model        string // model producing PoVs, recorded as provenance

	Workspace *Workspace

	Oracle oracle.Spec     // the spec run_pov applies
	Base   runner.RunSpec  // authoritative target run template
	Fixed  *runner.RunSpec // optional differential fixed-image template

	// Spawned holds sub-hypothesis statements the agent proposed via
	// spawn_hypothesis; the supervisor dispatches them as child lines (M2).
	Spawned []string

	// Outcome, updated by run_pov.
	Confirmed      bool
	LastVerdict    *oracle.Verdict
	LastResult     *verify.Result
	ConfirmedPoV   []byte // the exact PoV bytes that produced the passing verdict
	PoVSubmissions int
}

Session is the shared state one agent works against: the workspace it edits, the oracle + target run specs its PoVs are judged by, and the verdict it is trying to reach. The oracle-confirmed flag is what lets the Loop terminate on ground truth rather than on the model's say-so.

type TemplateCompactor

type TemplateCompactor struct{}

TemplateCompactor renders the digest deterministically from the durable records — no model call, zero extra tokens, most faithful. The default.

func (TemplateCompactor) Compact

type Tool

type Tool interface {
	Name() string
	Description() string
	Schema() json.RawMessage
	Invoke(ctx context.Context, args json.RawMessage) (string, error)
}

Tool is one capability the agent sees. Invoke returns the textual result the model observes next turn; a returned error is surfaced to the model as an error observation (the tight observe-error-retry cycle) rather than aborting the loop.

func Belt

func Belt(s *Session) []Tool

Belt returns the fixed M1 tool belt for a session: edit / read_file / ls / exec / run_pov. run_pov is the only tool that reaches a verdict.

type Workspace

type Workspace struct {
	Root string
	// ExecTimeout bounds a single exec call (default 60s).
	ExecTimeout time.Duration
	// MaxOutput caps captured stdout/stderr per exec (default 64 KiB).
	MaxOutput int
	// Sandbox, if set, runs exec inside an isolated container instead of on the
	// host (the isolation boundary). File tools stay host-side via os.Root; the
	// workspace is bind-mounted into the sandbox so both see the same files.
	Sandbox Sandbox
}

Workspace is the agent's scratch environment: a directory where it edits files (harnesses, PoV scripts) and runs helper commands. It is distinct from the oracle's air-gapped run container — exec here is the agent's own hands; run_pov submits to the authoritative judge. Path access is confined to the workspace root.

func NewWorkspace

func NewWorkspace(dir string) (*Workspace, error)

NewWorkspace creates (mkdir -p) a workspace rooted at dir.

func (*Workspace) Close

func (w *Workspace) Close() error

Close releases any sandbox backing the workspace (a no-op for host exec).

func (*Workspace) Exec

func (w *Workspace) Exec(ctx context.Context, name string, args []string, stdin string, timeout time.Duration) (ExecResult, error)

Exec runs a command inside the workspace with output caps and a timeout. It is the agent's general capability (compile, inspect, run helpers). It is not the oracle: nothing here reaches a verdict.

func (*Workspace) List

func (w *Workspace) List(rel string) ([]string, error)

List returns the entries under a workspace-relative directory.

func (*Workspace) ReadFile

func (w *Workspace) ReadFile(rel string) (string, error)

ReadFile reads a workspace-relative path (symlink-confined via os.Root).

func (*Workspace) WriteFile

func (w *Workspace) WriteFile(rel, content string) error

WriteFile writes content to a workspace-relative path, creating parent dirs. Confinement is enforced by os.Root: symlinks that would escape the workspace are refused, closing the TOCTOU/symlink hole that lexical cleaning alone left open (the agent's scratch space is not a path into the host).

Jump to

Keyboard shortcuts

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