agentsession

package
v0.8.0 Latest Latest
Warning

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

Go to latest
Published: Oct 14, 2026 License: Apache-2.0 Imports: 25 Imported by: 0

Documentation

Overview

Package agentsession is what a carried program's agents run on inside codeaf: an implementation of appx.App over the run's model API, with a small read-only agent loop ([Session]) for every agent session.

IT IS SHARED, AND IT IS CODEAF'S. It was written for sec-af, which was a node on a control plane: its plain model calls went to a router with a key it held, its agent sessions were a coding-agent binary it spawned once per call, and every call between its own reasoners was an HTTP round trip. Here all three are codeaf's: a model call goes to the run's model API (internal/provider/modelapi), the only road to a model a program codeaf carries has; an agent session is a loop in this process whose every turn is one call on that same road; and a call between reasoners stays in the process (internal/secaf/audit's local calls). Nothing in it is one program's, so it lives beside the programs that use it and not inside one of them: a change here reaches every program that runs on it.

IT CHANGES NOTHING IN THE FOLDER IT READS. A program that lands text promises the person their repository back exactly as it was, so the tools an agent session is given read and search and never write, and there is no shell to write with.

Index

Constants

This section is empty.

Variables

View Source
var ErrCeiling = errors.New("the run's dollar ceiling is reached, so codeaf made no call")

ErrCeiling is the run's dollar ceiling, reached: the model API made no call. It ends the program's model work everywhere at once, and what the program found before it is still reported.

Functions

This section is empty.

Types

type App

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

App is appx.App on codeaf.

func New

func New(client *Client, config Config) (*App, error)

New opens the App over the run's model API. It refuses a Config that leaves any figure unset: Sessions, Calls, MaxTurns, SessionWall and every field of Policy are the program's to state (Policy says why).

func (*App) AI

func (a *App) AI(ctx context.Context, prompt string, opts ...ai.Option) (*ai.Response, error)

AI makes one structured call. A served model that refuses a schema-bound answer is asked once more with the schema in words, which every model follows and sec-af's tolerant reading (internal/secaf/aix) parses.

func (*App) Call

func (a *App) Call(_ context.Context, target string, _ map[string]any) (map[string]any, error)

Call is never answered here: the program's calls between its own reasoners stay in the process (internal/secaf/audit's local calls), which wrap this App. A call that reached it is a reasoner nobody registered.

func (*App) Harness

func (a *App) Harness(ctx context.Context, prompt string, schema map[string]any, dest any, opts appx.HarnessOptions) (*appx.HarnessResult, error)

Harness runs one agent session over the repository. A session that ran and gave no answer of its schema is a result with IsError, as the old harness reported one; a session that could not run is the error.

func (*App) Note

func (a *App) Note(_ context.Context, message string, tags ...string)

Note is one of sec-af's progress notes.

func (*App) Spent

func (a *App) Spent() (cost float64, sessions, calls int)

Spent is what the program's calls have cost so far, as the replies priced them, and how many sessions and single calls it made. The model API's own meter is what bills the run; this is the program's own account of itself for its report.

type Client

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

Client is the run's model API as sec-af's calls reach it: one base URL, one token, and the ceiling's refusal remembered once it has been heard.

func NewClient

func NewClient(api delegate.ModelAPI, retries int) (*Client, error)

NewClient opens the run's model API. It refuses an API with no address or no token, because a program with neither has no road to a model at all.

retries is how many times a call that failed on the way is sent again, and it is the program's to state like every figure here (Policy says why); zero is no retry. The model API already walks codeaf's own ladder of services for every call, so a retry covers only what that cannot — a connection dropped on this machine, a moment the API was busy.

func (*Client) Complete

func (c *Client) Complete(ctx context.Context, request Request) (*ai.Response, error)

Complete sends one call and answers the model's reply.

func (*Client) Refusal

func (c *Client) Refusal() error

Refusal is the first refusal that stopped all model calls. A program that retains a partial result still needs the typed ceiling or key refusal, even if its own phase calls wrapped the individual failures in opaque errors.

type Config

type Config struct {
	// Root is the repository the run reads, absolute. No session reads
	// outside it, whatever a caller's options say.
	Root string
	// Work is what every session is one agent of, as a noun phrase, said in its
	// system prompt ([SessionOrder.Work]): sec's "a security audit".
	Work string
	// SessionModel answers the agent sessions — the hunters, tracers and
	// provers that read the code — and AIModel the single structured calls:
	// the duplicate checks, the exploit reasoning, the compliance mapping.
	// Empty leaves the choice to the model API, which answers on the run's
	// work seat.
	SessionModel, AIModel string
	// Sessions is how many agent sessions run at once across the whole run,
	// and Calls how many single calls. sec-af's own fan-outs multiply — four
	// hunters each enriching five locations — and the old harness capped its
	// processes at eight for the same reason.
	Sessions, Calls int
	// MaxTurns bounds one session's turns; sec-af's own default was fifty.
	MaxTurns int
	// SessionWall bounds one session's time; sec-af's harness waited thirty
	// minutes.
	SessionWall time.Duration
	// Policy is how every session reads and answers ([Policy]).
	Policy Policy
	// Limits is each agent's own turns and time, by the options its session
	// was started with; a zero field falls back to MaxTurns or SessionWall.
	// Nil gives every agent those two.
	//
	// EACH AGENT IS BOUNDED BY WHAT ITS OWN WORK TAKES. One cap for every agent
	// was too short for a scanner that walks the repository and far too long
	// for a profiler that reads three files: /review's reviewers spent fifty turns
	// at twenty seconds each where a dozen would have done, and sec's
	// scanners were the ones cut at fifty. The program knows its agents, so the
	// figures are its own.
	Limits func(appx.HarnessOptions) Limits
	// Watch hears every session, call and note as it ends, for the task's
	// page. Nil hears nothing.
	Watch *Watch
}

Config is how the App runs: where, on which models, and how many sessions at once.

type Limits

type Limits struct {
	Turns int
	Wall  time.Duration
	// AnswerReserve is part of Wall, not extra time. Zero preserves the
	// program's existing reading policy.
	AnswerReserve time.Duration
}

Limits is one agent session's bounds: the most turns it takes and the longest it runs. Zero is the App's own default for that bound.

type Policy

type Policy struct {
	// StructuredAnswers opts schema-bound final turns into native formatting
	// only when the profile's catalog publishes support. False keeps the
	// program's existing prompt-only behavior.
	StructuredAnswers bool
	// FollowUps is how many times an answer that does not meet its schema is
	// asked for again.
	FollowUps int
	// ContextChars is how much a session's transcript may hold before it is
	// told to stop reading and answer.
	ContextChars int
	// AnswerNow is what a session is told when its turns or its room run out:
	// to stop reading and answer with what it has.
	AnswerNow string
	// Tools is what one call of each read-only tool may answer.
	Tools ToolLimits
}

Policy is how every agent session of a program reads and answers.

type RefusedError

type RefusedError struct {
	// Ceiling is the run's dollar ceiling; otherwise the key was refused.
	Ceiling bool
	Code    int
	Message string
}

RefusedError is an answer that ends the run's model work: the ceiling (402) or a service that refused the key (401, 403). Nothing that follows it can succeed, so nothing is retried and the client answers it to every call after.

WHICH OF THE TWO IT IS IS DECIDED ONCE, where the answer arrives, and kept as a fact: nothing downstream reads the status to decide again (internal/taxonomy's law).

func (*RefusedError) Error

func (e *RefusedError) Error() string

func (*RefusedError) Is

func (e *RefusedError) Is(target error) bool

Is makes the ceiling match ErrCeiling.

type Request

type Request struct {
	Model    string
	Thread   string
	Messages []ai.Message
	Tools    []ai.ToolDefinition
	// ResponseFormat asks for a JSON answer of one schema; nil is free text.
	ResponseFormat *ai.ResponseFormat
}

Request is one model call: who is asking (the thread its turns are kept under on the task's page), what, and in what shape the answer must come.

type SessionOrder

type SessionOrder struct {
	Model  string
	Thread string
	// Root is the repository, absolute; every tool reads only below it.
	Root string
	// Work is what the session is one agent of, as a noun phrase: sec's
	// "a security audit". Empty says only that it works on the repository.
	Work   string
	Prompt string
	// Schema is the JSON Schema the answer must meet; nil is a free-text
	// answer, which sec-af's chain correlation asks for.
	Schema   map[string]any
	MaxTurns int
	// Wall is the session's own time bound; zero is none past the context's.
	Wall time.Duration
	// AnswerReserve stops reading this long before the existing deadline, so
	// the session can answer from completed observations. Zero reserves none.
	AnswerReserve time.Duration
	// Policy is how the session reads and answers; every limit is required.
	Policy Policy
	// contains filtered or unexported fields
}

SessionOrder is one session: who answers, where it reads, what it is asked, the shape of its answer, and its bounds.

type SessionResult

type SessionResult struct {
	// Text is the answer as the model wrote it.
	Text string
	// JSON is the answer's object when a schema was asked for and met.
	JSON json.RawMessage
	// Failed is why a session that ran did not give an answer of the shape
	// asked for; empty for one that did.
	Failed   string
	Turns    int
	Tools    int
	CostUSD  float64
	Duration time.Duration
}

SessionResult is how a session ended: its answer, and what it cost.

func RunSession

func RunSession(ctx context.Context, client *Client, order SessionOrder) (SessionResult, error)

RunSession runs one session to its answer. The error is for a session that could not run at all — the model API refused, the run was stopped; a session that ran and gave no usable answer says so in Failed.

type ToolLimits

type ToolLimits struct {
	// ReadLines and ReadBytes are the most lines and bytes one read_file
	// answers; ReadLineRunes the most of one line it shows.
	ReadLines, ReadBytes, ReadLineRunes int
	// ListEntries is the most entries one list_dir answers, and GlobMatches
	// the most paths one glob answers.
	ListEntries, GlobMatches int
	// GrepMatches is the most lines one grep answers, GrepFileBytes the
	// largest file it reads, and GrepLineRunes how much of a matching line it
	// quotes.
	GrepMatches, GrepFileBytes, GrepLineRunes int
}

ToolLimits is the most one call of each tool answers.

type Watch

type Watch struct {
	// Session is one agent session that ended: its label (the agent that ran
	// it, such as `hunt-scan`), how it went, and the error that stopped it
	// before it could answer.
	Session func(label string, result SessionResult, err error)
	// Note is one of sec-af's progress notes, with its tags.
	Note func(message string, tags []string)
	// Call is one single structured call that ended: the title of the schema
	// its answer took (`VerdictDecision`), and the error that stopped it.
	Call func(schema string, err error)
}

Watch is what the program's progress is told to.

Directories

Path Synopsis
Package appx declares the agent-capability seam every reasoner and phase of a carried program depends on: sec's first, which it was written for.
Package appx declares the agent-capability seam every reasoner and phase of a carried program depends on: sec's first, which it was written for.

Jump to

Keyboard shortcuts

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