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 ¶
- Variables
- type App
- func (a *App) AI(ctx context.Context, prompt string, opts ...ai.Option) (*ai.Response, error)
- func (a *App) Call(_ context.Context, target string, _ map[string]any) (map[string]any, error)
- func (a *App) Harness(ctx context.Context, prompt string, schema map[string]any, dest any, ...) (*appx.HarnessResult, error)
- func (a *App) Note(_ context.Context, message string, tags ...string)
- func (a *App) Spent() (cost float64, sessions, calls int)
- type Client
- type Config
- type Limits
- type Policy
- type RefusedError
- type Request
- type SessionOrder
- type SessionResult
- type ToolLimits
- type Watch
Constants ¶
This section is empty.
Variables ¶
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 ¶
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 ¶
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 ¶
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.
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 ¶
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.
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. |