Help us shape the future of Go! Take the 2026 Go Developer Survey

view

package
v0.3.0 Latest Latest
Warning

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

Go to latest
Published: Oct 2, 2026 License: MIT Imports: 8 Imported by: 0

Documentation

Overview

Package view builds read models over a store for operator surfaces: what runs exist, what a run did, and which approval is waiting. Everything here reads; nothing decides. The summaries are the shape cmd/agentrt prints and both consumers' own commands report.

Index

Examples

Constants

View Source
const MaxFieldBytes = 2000

MaxFieldBytes bounds a single string value embedded in an approval's capability, presentation, or request arguments: all three come from a model or a policy, not from this package, so an operator surface that prints or encodes one uses this same limit whether it is writing text or JSON. cmd/agentrt's approval prompt applies it per line; BoundApproval applies it to JSON output the same way, so JSON does not carry more than the text an operator already sees.

Variables

View Source
var (
	ErrNotPending        = errors.New("approval is not pending")
	ErrNoPendingApproval = errors.New("no pending approval")
	ErrAmbiguousApproval = errors.New("more than one pending approval")
)

Errors PendingApproval wraps, so a front end can match them and word its own message; the text carries the ids.

View Source
var (
	// ErrRunNotWaiting means the run is not (or no longer) waiting for
	// approval: another decision, a resume, or an expiry already moved it
	// on.
	ErrRunNotWaiting = errors.New("run is not waiting for approval")
	// ErrApprovalDecided means the named approval was already approved,
	// rejected, or expired; it is not the caller's to decide again.
	ErrApprovalDecided = errors.New("approval is already decided")
	// ErrDatabaseLocked means another process holds the database open for
	// writing. It wraps the driver's own error, whose text carries the raw
	// SQLite code, so a front end can say something a person can act on.
	ErrDatabaseLocked = errors.New("database is locked by another consumer")
)

Errors decideError wraps around whatever agentrt.Approve, agentrt.Reject, and agentrt.Cancel report, so a caller such as cmd/agentrt can match the shape of a failure instead of parsing its text. The core functions return plain fmt.Errorf values with no sentinel of their own; these give one.

Functions

func Approve added in v0.3.0

func Approve(ctx context.Context, store *agentrt.Store, obs agentrt.Observer, runID, approvalID, by, note string) error

Approve records an approval decision, wrapping the core's error so ErrRunNotWaiting, ErrApprovalDecided, and ErrDatabaseLocked can be matched with errors.Is.

func ApproveShown added in v0.3.0

func ApproveShown(ctx context.Context, store *agentrt.Store, obs agentrt.Observer, runID, approvalID, shownHash, by, note string) error

ApproveShown is Approve for a front end that printed the approval before asking for a decision: shownHash must be the hash of the approval as printed (agentrt.Approval.Hash). If the stored approval no longer matches that hash, the decision is refused with agentrt.ErrApprovalChanged rather than applied to whatever the approval has since become. Wrapped the same way as Approve.

func BoundApproval added in v0.3.0

func BoundApproval(a agentrt.Approval) agentrt.Approval

BoundApproval returns a with Capability, Presentation, and Request.Args passed through BoundRaw: an approval built from a model's tool call and a policy's presentation can carry a value of any size, so a command encoding it as JSON bounds it the same way its text output does, rather than a large or crafted database exhausting a consumer's memory or disk just because it asked for JSON instead of text.

func BoundRaw added in v0.3.0

func BoundRaw(raw json.RawMessage) json.RawMessage

BoundRaw decodes raw as JSON, applies BoundValue, and re-encodes it. raw is returned unchanged if it is empty or does not round-trip through JSON, which only happens for a value that is already malformed for some other reason and is left for that error to surface elsewhere.

func BoundValue added in v0.3.0

func BoundValue(v any) any

BoundValue returns v with every string longer than MaxFieldBytes, anywhere inside it, replaced by its head and tail and the number of bytes omitted between them. Maps and slices are walked recursively so the bound applies to a value nested inside an argument object, not only a bare string; anything else (numbers, bools, null) passes through unchanged.

func Cancel added in v0.3.0

func Cancel(ctx context.Context, store *agentrt.Store, obs agentrt.Observer, runID, by, note string) error

Cancel ends a run, wrapping errors the same way Approve does.

func PendingApproval

func PendingApproval(ctx context.Context, store *agentrt.Store, runID, approvalID string) (agentrt.Approval, error)

PendingApproval selects the approval an operator means. With an id it fetches that approval and requires it to be pending; without one the run must have exactly one pending approval, and the error names the candidates when it does not, because guessing which one to grant is not the runtime's decision to make.

Example

PendingApproval is the rule for choosing the approval an operator means: without an id the run must have exactly one pending approval, and a named approval must still be pending. Guessing is not the runtime's decision.

ctx := context.Background()
store, err := agentrt.OpenStore(":memory:")
if err != nil {
	panic(err)
}
defer store.Close()

push := tool{spec: agentrt.ToolSpec{
	Name: "push", Description: "push", SideEffect: agentrt.RemoteMutation, Timeout: time.Second,
	InputSchema: []byte(`{"type":"object","properties":{"n":{"type":"integer"}},"additionalProperties":false}`),
}}
agent := &scripted.Agent{Decisions: []agentrt.Decision{scripted.ToolCall("push", `{"n":7}`, "publish")}}
d, err := agentrt.NewDriver(agentrt.Config{
	Store: store, Agent: agent, Policy: agentrt.DefaultPolicy(),
	Tools: []agentrt.Tool{push},
	NewID: func() func() string {
		n := 0
		return func() string { n++; return fmt.Sprintf("id-%d", n) }
	}(),
})
if err != nil {
	panic(err)
}
run, err := d.Start(ctx, "publish the counter", agentrt.DefaultLimits())
if err != nil {
	panic(err)
}

// One pending approval, so the operator need not name it.
a, err := view.PendingApproval(ctx, store, run.ID, "")
fmt.Println(a.ID, a.Kind, a.Status, err)

if err := d.Approve(ctx, run.ID, a.ID, "joey", ""); err != nil {
	panic(err)
}

// Once decided it is no longer a candidate, by id or without one.
_, err = view.PendingApproval(ctx, store, run.ID, a.ID)
fmt.Println(errors.Is(err, view.ErrNotPending))
_, err = view.PendingApproval(ctx, store, run.ID, "")
fmt.Println(errors.Is(err, view.ErrNoPendingApproval))
Output:
id-3 remote_mutation pending <nil>
true
true

func Reject added in v0.3.0

func Reject(ctx context.Context, store *agentrt.Store, obs agentrt.Observer, runID, approvalID, by, note string) error

Reject records a rejection, wrapping errors the same way Approve does.

func RejectShown added in v0.3.0

func RejectShown(ctx context.Context, store *agentrt.Store, obs agentrt.Observer, runID, approvalID, shownHash, by, note string) error

RejectShown is ApproveShown for a rejection.

Types

type RunDetail added in v0.3.0

type RunDetail struct {
	Run       RunSummary
	Steps     []StepSummary
	Approvals []agentrt.Approval
}

RunDetail is one run with its steps and every approval: what a command that shows a run prints.

func Detail added in v0.3.0

func Detail(ctx context.Context, store *agentrt.Store, runID string) (RunDetail, error)

Detail reads a run, its steps, and its approvals, each once.

type RunDetailPage added in v0.3.0

type RunDetailPage struct {
	Run            RunSummary
	Steps          []StepSummary
	StepsTotal     int
	Pending        []agentrt.Approval
	PendingTotal   int
	Approvals      []agentrt.Approval
	ApprovalsTotal int
	Waiting        *agentrt.Approval
}

RunDetailPage is one run read in pages: the run with its text capped, a page of its steps, a page of its pending approvals, and a page of all its approvals, each at most limit after skipping offset, with their totals. Waiting is the run's pending approval when it has exactly one, whatever the offset. Every read is bounded (the agentrt.Store page reads), so a front end can show a run of a database it does not trust.

func DetailPage added in v0.3.0

func DetailPage(ctx context.Context, store *agentrt.Store, runID string, limit, offset int) (RunDetailPage, error)

DetailPage reads a RunDetailPage.

type RunSummary

type RunSummary struct {
	ID           string                 `json:"id"`
	Goal         string                 `json:"goal"`
	Status       agentrt.RunStatus      `json:"status"`
	Reason       agentrt.TerminalReason `json:"reason,omitempty"`
	ReasonDetail string                 `json:"reason_detail,omitempty"`
	Steps        int                    `json:"steps"`
	CreatedAt    time.Time              `json:"created_at"`
	// FinishedAt uses omitzero, not omitempty: time.Time is a struct, and
	// omitempty never treats a struct as empty, so a zero time.Time would
	// otherwise be marshaled as its zero-value RFC 3339 rendering.
	FinishedAt time.Time `json:"finished_at,omitzero"`
	// PendingApprovalID is set only for a run waiting for approval, which is
	// the only state in which an approval can still be decided.
	PendingApprovalID string         `json:"pending_approval_id,omitempty"`
	ModelCalls        int            `json:"model_calls,omitempty"`
	Usage             agentrt.Usage  `json:"usage,omitzero"`
	EstimatedCost     agentrt.Micros `json:"estimated_cost_micros,omitempty"`
}

RunSummary is one run as an operator needs to see it. The model totals come from the run row itself, so a summary costs no extra query.

func Runs

func Runs(ctx context.Context, store *agentrt.Store) ([]RunSummary, error)

Runs summarizes every run, newest first, in two queries: the runs, and the pending approvals of every waiting run.

func RunsPage added in v0.3.0

func RunsPage(ctx context.Context, store *agentrt.Store, limit, offset int) ([]RunSummary, int, error)

RunsPage summarizes at most limit runs, newest first, after skipping offset, and returns how many runs there are. Every read is bounded by the page (agentrt.Store.ListRunsPage, PendingApprovalIDsOf), so a front end can list a database it does not trust.

func Summary

func Summary(ctx context.Context, store *agentrt.Store, runID string) (RunSummary, error)

Summary summarizes one run. It reads the run and at most two pending approval ids.

type StepSummary

type StepSummary struct {
	Index       int                     `json:"index"`
	ID          string                  `json:"id"`
	Tool        string                  `json:"tool,omitempty"`
	Decision    agentrt.DecisionKind    `json:"decision,omitempty"`
	Status      agentrt.StepStatus      `json:"status"`
	Policy      agentrt.PolicyOutcome   `json:"policy,omitempty"`
	Observation agentrt.ObservationKind `json:"observation,omitempty"`
	Summary     string                  `json:"summary,omitempty"`
}

StepSummary is one step: what the agent asked for, what the policy said, and what came back.

func Steps

func Steps(ctx context.Context, store *agentrt.Store, runID string) ([]StepSummary, error)

Steps summarizes a run's steps in index order.

Jump to

Keyboard shortcuts

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