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

view

package
v0.0.14 Latest Latest
Warning

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

Go to latest
Published: Oct 18, 2026 License: MIT Imports: 10 Imported by: 1

Documentation

Overview

Package view is the reconciler: a pure model that takes the record's changes and the agent's live events, in whatever order they arrive, and produces what a client renders.

The rule is the plan's: the record is the truth for everything committed, and live events cover only what is not. So a rendering is the path's items from the record, then an overlay of what is in flight on top: items still streaming, calls opened and not answered, the state of the turn, the permissions that are out. An overlay item is dropped when the entry carrying it lands, matched on the item's ID within its response, or on the call ID for a function call and its output.

Order independence

The record and the live stream reach a client through two paths, and an entry is written before the live event that follows it is queued, so a follower can deliver the entry first. The view therefore remembers what has landed and ignores a live event for an item that has: an entry never leaves a ghost behind it. A landed item is shown once, from the record; an overlay item is shown once, until its entry lands.

The recorder's lags

The overlay waits for the entry, never for the event.

  • A live item_end is not a commit. A model item that completes before the stream names its response is written at response_end, so its overlay copy stays through agentturn.ItemEnd and agentturn.ResponseEnd until the entry lands.
  • A config entry is written by the next entry-writing event, so the record's model lags the model of the turn running. Both are reported: Model.Config is the record's, Turn.Model the turn's.
  • A run's end flushes what is left of its overlay, but only once the record holds the run's end entry as well, which is written after everything the run wrote. Flushing at the live event alone would drop an item whose entry the follower has not delivered yet.

Limits

An item with no ID, which only the loop appends (a prompt, a steered message), has no key to reconcile on and is never overlaid: the recorder writes it in the barrier of its item_end, so it is on the record before the model is called. A function call output is not overlaid either; the call row carries the result until the output lands. A model that streams items with no ID would not show them live.

A View is not safe for concurrent use; a client drives it from one goroutine.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Usage

Usage sums the token usage of every model call on a path, split by the model each call was made under. It counts responses and the folds, failed or not.

Types

type Branch

type Branch struct {
	// Leaf is the item the branch rests on (Model.Leaves holds the same
	// IDs).
	Leaf string
	// Label names the branch by its newest message: who wrote it ("agent"
	// for the assistant role) and its first words. A branch with no
	// message on it is labelled "(no message)".
	Label string
	// Role is the role of that message.
	Role string
	// RunID is the run of the nearest run entry at or behind the tip.
	RunID string
	// Time is when the tip entry was written.
	Time time.Time
	// Current is set for the branch the view is on.
	Current bool
}

Branch is one branch tip of the session, for a tree view.

type Call

type Call struct {
	CallID string
	Name   string
	Args   string
	// Schema is the parameters schema of the tool the call names, as the
	// config in force where the call sits on the line holds it: the tool
	// the call was made against, which a later config may have changed
	// or dropped. A live call takes the config in force at the end of
	// the line. It is nil when the config names no such function tool,
	// and for a child call, which another agent's tools made.
	Schema json.RawMessage
	State  CallState
	// Partial is the latest progress of a running tool.
	Partial string
	// Output is what the model sees, once the call has ended.
	Output string
	// Committed is set when the output is on the record, so the call is
	// answered for good; before that Output is live.
	Committed bool
	// Verdict and Reason are the last policy decision the record holds
	// for the call, and Reason the question of a deferred one.
	Verdict string
	Reason  string
	// Children are the calls this call's tool made, live only.
	Children []Call
}

Call is how a function call stands.

type CallState

type CallState int

CallState says how far a call got.

const (
	// CallOpen: the call is on the record or the stream and has not
	// reached its tool.
	CallOpen CallState = iota
	// CallRunning: the call was handed to its tool.
	CallRunning
	// CallEnded: the call ended and Output holds what the model sees.
	CallEnded
	// CallBlocked: a policy refused the call; Output says why.
	CallBlocked
	// CallDeferred: the call waits for an answer.
	CallDeferred
	// CallCutOff: an abort or a failure ended the run with the call
	// unanswered. It is not a question; the next prompt answers it.
	CallCutOff
)

Call states.

func (CallState) String

func (s CallState) String() string

String names the state.

type Fold

type Fold struct {
	// FirstKept is the entry the context keeps from: everything before it
	// on the path is replaced by the summary in the model's request, and
	// stays on the record.
	FirstKept string
	// SummaryLen is the summary's text length in runes.
	SummaryLen int
	// Pinned is how many items the compaction carries over whole.
	Pinned int
	// TokensBefore is the estimate that triggered the fold, 0 if not
	// recorded.
	TokensBefore int
}

Fold is what a compaction entry says it folded.

type Link struct {
	// Rel is the link's relation: agentsession.RelSubsession, RelPeer,
	// RelContinuedIn, RelForkOf or RelJudgedBy.
	Rel string
	// Session is the other session. It is empty for a callee in another
	// system that published no session ID.
	Session string
	// CallID is the call that spawned a subsession or reached a peer,
	// when the link says.
	CallID string
	// Entry is the link entry's own ID.
	Entry string
	// URL, Task and Context name a callee in another system, reached
	// over a protocol such as A2A, on a subsession or peer link: the
	// endpoint the call was sent to, the callee's task (empty when it
	// answered without one) and its conversation. URL is empty for a
	// subsession of this store.
	URL     string
	Task    string
	Context string
	// CancelSent is set when a cancel notice names this link: the caller
	// told the callee it no longer wanted the work and did not wait. It
	// does not say the work stopped.
	CancelSent bool
}

Link is a link entry of the session: a subsession it spawned, a callee in another system, a successor it was continued in, a fork or a judge. A cancel notice is not a link of its own: it marks the callee link it names (CancelSent).

type Model

type Model struct {
	// Session is the followed session's ID.
	Session string
	// Leaf is the entry the rendered path ends at, and Leaves every
	// branch tip the session has.
	Leaf   string
	Leaves []string
	// Tail is the last entry of the viewed line: the leaf and the
	// bookkeeping behind it. The line is Session.Path(Tail).
	Tail string
	// Entries is how many entries the session held at this step.
	Entries int
	// Branches describes the tips a tree shows, newest first. They are
	// Leaves less a leaf label that only marks a head (see branchTips),
	// plus the viewed line.
	Branches []Branch
	// Links are the session's link entries, on any branch: its
	// subsessions, its peers, its successor. A cancel notice is folded
	// into the link it names.
	Links []Link
	// Origin is where the session came from, by its header.
	Origin Origin
	// Config is the model in force at the leaf, by the record.
	Config string
	// Usage is the token usage of the viewed line's model calls, and
	// UsageByModel the same split by the model each call was made under.
	Usage        openresponses.Usage
	UsageByModel map[string]openresponses.Usage
	// Run is the last run on the viewed line, by the record, and Worked
	// how long the line's runs that ended took, summed: the session's
	// time working.
	Run    Run
	Worked time.Duration
	// Rows is the path's visible items, then the live rows.
	Rows        []Row
	Turn        Turn
	Permissions []Permission
	// CutOff lists the calls the last run left unanswered without asking
	// anyone: it was aborted or failed. They are shown as cut off.
	CutOff []Permission
	// Questions are the questions running calls wait on the user for,
	// in the order they were asked: a sub-agent's call its policy asks
	// about, a tool's own question.
	Questions []agentturn.Question
	// Queued are the inputs waiting to join the conversation, in the
	// order they were accepted, from the record's path to its head
	// (agentsession.Queued). An input a run did not take before it
	// ended is written again after the run's end by the recorder, so it
	// stays listed until the next run appends it.
	Queued []Queued
}

Model is what a client renders.

func At

func At(s *agentsession.Session, leaf string) Model

At renders the line of s that ends at leaf, as a view attached to the session with its head there would, and nothing live: no overlay, and no turn. It reads s and keeps nothing, so a caller can pass a session it read for the purpose, the session a fork came from or one a link names, and show it without following it. Its Permissions and CutOff are the record's reading of the line, which a read-only display does not offer to answer.

type Origin

type Origin struct {
	// ParentSession is the header's parent_session: the origin of a
	// fork, the predecessor of a continuation, or the parent of a
	// subsession.
	ParentSession string
	// Base is the entry of ParentSession the session was forked at, "" for
	// a session that is not a fork.
	Base string
	// SpawnedBy is the header's spawned_by: the call that spawned the
	// session, for a subsession.
	SpawnedBy string
}

Origin is where a session came from, from its header: the session it was forked, continued or spawned from, and the entry it was forked at.

type Permission

type Permission struct {
	CallID string
	Name   string
	Args   string
	// Question is the reason the call was deferred, as a front puts it
	// to the user.
	Question string
	// Subject is the part of the call the question is about, when the
	// policy decided on a part (one command of a compound line), from
	// the decision that deferred it; "" for the whole call. The record
	// does not hold it, so a permission read from the record alone has
	// none.
	Subject string
	// MayHaveRun is set for a call that was handed to its tool before
	// it was left unanswered: a call held after its dispatch, or one an
	// abort cut off while it ran. Its effects may have happened.
	MayHaveRun bool
	Reason     string
}

Permission is a call that waits for the caller.

type PriceSummary

type PriceSummary struct {
	// Total is the line's cost in US dollars, valid when Priced is true.
	Total float64
	// Priced says every model with usage on the line was priced.
	Priced bool
	// Unpriced lists the models the cost source has no price for, sorted.
	Unpriced []string
}

func Price

func Price(byModel map[string]openresponses.Usage, cost client.Cost) PriceSummary

Price prices one model call under cost. Cost hooks are linear over the usage they are given (the price table's are), so each model's aggregate usage is priced once. A nil cost leaves the line unpriced and unnamed.

type Queued added in v0.0.8

type Queued struct {
	// EntryID is the queued entry.
	EntryID string
	// Mode is agentsession.ModeSteer or agentsession.ModeFollowUp.
	Mode string
	Item openresponses.Item
}

Queued is an input the agent accepted into its queue, a steer or a follow-up, that the conversation does not hold yet: a queued entry on the record that no item entry names as its source. It leaves the list in the same step its item becomes a row, so it is never shown twice.

type Row

type Row struct {
	// EntryID is the entry that holds the item, and "" while the item is
	// live only.
	EntryID string
	// Live is set for an overlay row: not on the record yet.
	Live bool
	// Open is set for a live row still streaming.
	Open bool
	// KeptFromModel marks a row the record holds as a custom entry: an
	// item the filter kept out of the model's context. It is on the
	// record, so it is shown, and a renderer may say it was not sent.
	KeptFromModel bool
	ResponseID    string
	Item          openresponses.Item
	// Call is set on the row of a function call.
	Call *Call
	// Fold is set on the row of a compaction entry: the point where the
	// record folds what came before into a summary, which Item carries.
	Fold *Fold
}

Row is one line of the conversation.

type Run

type Run struct {
	ID      string
	Started time.Time
	// Ended is zero while the line holds no end entry for the run.
	Ended time.Time
	Usage openresponses.Usage
}

Run is a run on a line, by the record: when its start entry and its end entry were written, and the token use of its model calls so far. A client shows it as the current turn's figures.

type Turn

type Turn struct {
	State TurnState
	RunID string
	// Number is the turn within the run, from 1, and 0 between runs.
	Number int
	// Model is the model the turn is calling, which can be ahead of
	// [Model.Config]: the config entry is written by the next
	// entry-writing event.
	Model string
	// Withheld is set when the last run ended because a guard withheld
	// a message: the text that streamed was never committed.
	Withheld bool
	// Cause is what stopped the last run, when it ended with
	// [agentturn.ReasonStopped] (a turn limit, a hook, a guard, a tool
	// that ended it, a refusal), and "" otherwise. It is the live end's:
	// the record does not hold it.
	Cause agentturn.StopCause
	// Err is what failed the last run, when it ended with
	// [agentturn.ReasonError], and nil otherwise: the live end's.
	Err error
	// Attempt is the number of the model call that failed and is being
	// retried, 0 when none is.
	Attempt int
}

Turn is the state of the run, from live events alone.

type TurnState

type TurnState int

TurnState says what the agent is doing.

const (
	// Idle: no run is going and nothing waits.
	Idle TurnState = iota
	// Running: a run is going.
	Running
	// RequiresAction: the last run ended with calls waiting for an
	// answer, which [Model.Permissions] lists.
	RequiresAction
)

Turn states.

func (TurnState) String

func (s TurnState) String() string

String names the state.

type View

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

View reconciles the record with the live stream.

func New

func New() *View

New returns an empty view.

func (*View) Live

func (v *View) Live(ev agentturn.Event)

Live applies a live event: one of agentturn's, as a client.Source delivers it and client.Copy copies it. The view keeps what it is given, so an event must share nothing the agent goes on changing. An event the view does not read is ignored.

func (*View) Model

func (v *View) Model() Model

Model returns what to render now. It shares nothing with the view.

func (*View) Record

func (v *View) Record(ch agentsession.Change)

Record applies a change from the record's follow.

Jump to

Keyboard shortcuts

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