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 ¶
- func ToolDefs(tools []Tool) []model.ToolDef
- type CompactInput
- type Compactor
- type Config
- type ContainerSandbox
- type ExecResult
- type ModelCompactor
- type Outcome
- type ReAct
- type Sandbox
- type Session
- type TemplateCompactor
- type Tool
- type Workspace
- func (w *Workspace) Close() error
- func (w *Workspace) Exec(ctx context.Context, name string, args []string, stdin string, ...) (ExecResult, error)
- func (w *Workspace) List(rel string) ([]string, error)
- func (w *Workspace) ReadFile(rel string) (string, error)
- func (w *Workspace) WriteFile(rel, content string) error
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
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 ¶
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 ¶
ExecResult is the outcome of one command.
type ModelCompactor ¶
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.
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 ¶
func (TemplateCompactor) Compact(_ context.Context, in CompactInput) ([]model.Message, error)
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.
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 ¶
NewWorkspace creates (mkdir -p) a workspace rooted at dir.
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) ReadFile ¶
ReadFile reads a workspace-relative path (symlink-confined via os.Root).
func (*Workspace) WriteFile ¶
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).