Documentation
¶
Overview ¶
Package agentsession implements the Agent Session Format: an append-only, tree-structured JSONL record of one agent session whose conversation payloads are Open Responses items. The format is specified in docs/rfcs/0001-agent-session-format.md; this package is its reference implementation.
Shape ¶
A session file is a header line followed by entries, one JSON object per line. An entry's id is the hash of its envelope over the hash of its body, and parent names a parent by hash, so a file verifies itself and a leaf commits to its whole path; Session.Append computes both. Entries form a tree through those members, so a branch is a child of an earlier entry, in place, and an abandoned branch stays in the file. A session made with Fork continues from an entry of another, its base, and opens with the path to it. An entry may also name further predecessors in EntryBase.Parents — a subagent's result, a branch merged back — which record where converged work came from and are never walked when building a context. Header and Entry are the two line shapes; the concrete entry types are ItemEntry (one Open Responses item, verbatim), ResponseEntry (the envelope of one model call), ConfigEntry (a delta to request settings), CompactionEntry and BranchSummaryEntry (summaries that enter context), and the out-of-context LabelEntry, InfoEntry, EnvEntry, OutcomeEntry, LinkEntry and CustomEntry. Namespaced types decode to UnknownEntry and are written back byte for byte, as are namespaced items inside an item entry.
Sessions ¶
Session is the tree in memory: Read loads one, Write emits one, and Session.Append adds an entry as a child of the current leaf. Session.Branch moves the leaf so the next append forks; a fresh root follows Session.ResetLeaf. A file cut short by a crash still loads, and Session.Truncated reports the broken line.
Context ¶
Session.ContextAt runs the format's context algorithm: it walks the path to an entry, replays config entries into Settings, applies the last compaction and returns the item list a model call there would receive. Settings.Request turns that into the canonical openresponses.Request, and RequestHash hashes it as the format defines (RFC 8785 canonical JSON, SHA-256). Session.Verify checks a stored response's hash against the rebuilt request, and reports ErrNoHash rather than nil when the response recorded none. Session.RequestHashFor computes, by the same code, the hash a response not yet appended would verify with, which a redactor gives each redacted response (Session.SetRequestHash).
Stores ¶
Store is the persistence interface; MemoryStore is the in-memory one and the jsonl subpackage is the file store. The storetest subpackage is the conformance suite every store runs.
Export ¶
The export subpackage turns a session's root-to-leaf paths into ATIF documents, using the types in the atif subpackage, with the raw items carried in the extras so nothing is lost.
Index ¶
- Constants
- Variables
- func CanonicalRequest(req openresponses.Request) (openresponses.Request, error)
- func CanonicalTime(t time.Time) string
- func CheckCreate(h Header, redactedOnly bool) error
- func CheckHoldsRedacted(ctx context.Context, st Store) error
- func CheckImportMedia(h Header, origin *Session) error
- func CheckRedacted(h Header, entries []Entry) error
- func CheckRedactedIn(s *Session, entries []Entry) error
- func ClearRedacted(e Entry) error
- func ComputeReason(path, segment []Entry) string
- func ComputeSource(r *Run) string
- func EntryHashes(data []byte) (id, content string, err error)
- func GateEntry(format string, e Entry)
- func GitRevision(dir string) (string, error)
- func HashBytes(data []byte) string
- func HashFile(path string) (string, error)
- func HashFiles(dir string, paths ...string) (map[string]string, error)
- func HashRequestJSON(data []byte) (string, error)
- func HashText(s string) string
- func HeadMove(e Entry, holds func(id string) bool) bool
- func HeaderIdent(h Header) (string, error)
- func InContext(e Entry) bool
- func IsExtension(typ string) bool
- func JoinInstructions(parts []InstructionPart) string
- func MarshalEntry(e Entry) ([]byte, error)
- func NewBaseRuleError(detail string) error
- func NewEntryID() string
- func NewSessionID() string
- func OriginDispatches(ctx context.Context, r Reader, s *Session, callEntry string) (*Session, []*DispatchEntry, error)
- func ParseCanonicalTime(s string) (time.Time, bool)
- func ParseFormat(format string) (major, minor int, err error)
- func ReadSidecarBlob(media fs.FS, hash string) ([]byte, error)
- func ReadableIn(ctx context.Context) func(session string) bool
- func RecordResponse(ctx context.Context, store Store, sessionID string, req openresponses.Request, ...) (string, error)
- func RedactedSessionID(original, profile string) string
- func Redactions(ctx context.Context, st Store, id string) iter.Seq2[Summary, error]
- func RefConflict(name string, existing []string) error
- func RemapHeaderRefs(h Header, m Remap) (Header, []Reference)
- func RequestHash(req openresponses.Request) (string, error)
- func ResolveCurrent(ctx context.Context, st Store, name string) (id string, moved bool, err error)
- func SameWorkspace(a, b *Workspace) bool
- func SessionBlobs(s *Session) ([]string, error)
- func SetRedacted(e Entry, spans []Span) error
- func SidecarBlobs(e Entry) ([]string, error)
- func SubsessionID(parentSessionID, callID string) string
- func ToolName(t openresponses.Tool) string
- func ValidHash(s string) bool
- func ValidRefName(name string) error
- func ValidRefPrefix(prefix string) error
- func WithReadable(ctx context.Context, readable func(session string) bool) context.Context
- func Write(w io.Writer, s *Session) error
- type BlobReader
- type BlobSweeper
- type BlobWriter
- type BranchSummaryEntry
- type Call
- func (c *Call) Answered() bool
- func (c *Call) Args() string
- func (c *Call) DispatchedArgs() string
- func (c *Call) Failed() bool
- func (c *Call) From() []EntryRef
- func (c *Call) Held() bool
- func (c *Call) ID() string
- func (c *Call) IdempotencyKey() string
- func (c *Call) InFlight() bool
- func (c *Call) Pending() bool
- func (c *Call) Rejected() bool
- func (c *Call) State(h Header) CallState
- type CallState
- type Callee
- type CalleeLink
- type Change
- type ChangeKind
- type CompactionEntry
- type ConfigEntry
- type Context
- type Cursor
- type CustomEntry
- type DecisionEntry
- type DispatchEntry
- type Entry
- type EntryBase
- type EntryRef
- type EnvEntry
- func (e *EnvEntry) AddRead(path, hash string)
- func (e *EnvEntry) AddTool(name, version string)
- func (e *EnvEntry) AddWritten(path, hash string)
- func (*EnvEntry) EntryType() string
- func (e *EnvEntry) MarshalJSON() ([]byte, error)
- func (e *EnvEntry) SetGit(dir string, dirty bool) error
- func (e *EnvEntry) SetWorkspace(kind, ref string) *Workspace
- func (e *EnvEntry) UnmarshalJSON(data []byte) error
- type FileHashes
- type Follower
- type Harness
- type HeadStore
- type Header
- type Importer
- type InfoEntry
- type InstructionPart
- type ItemEntry
- type LabelEntry
- type LinkEntry
- func Judges(path []Entry) []*LinkEntry
- func NewCalleeSubsessionLink(callID string, c Callee) *LinkEntry
- func NewCancelSentLink(link *LinkEntry) *LinkEntry
- func NewJudgedByLink(judge, target string) *LinkEntry
- func NewLinkEntry(rel, session string) *LinkEntry
- func NewPeerLink(callID string, c Callee) *LinkEntry
- func NewSubsessionLink(session, callID string) *LinkEntry
- type LinkError
- type ListFilter
- type MediaImporter
- type MemoryOption
- type MemoryStore
- func (m *MemoryStore) Append(ctx context.Context, sessionID string, e Entry) (string, error)
- func (m *MemoryStore) Blob(ctx context.Context, hash string) ([]byte, error)
- func (m *MemoryStore) Create(ctx context.Context, h Header) (*Session, error)
- func (m *MemoryStore) Delete(_ context.Context, id string) error
- func (m *MemoryStore) Follow(ctx context.Context, id string, from Cursor) iter.Seq2[Change, error]
- func (m *MemoryStore) Import(ctx context.Context, r io.Reader, asRecord bool) (*Session, error)
- func (m *MemoryStore) List(_ context.Context, f ListFilter) iter.Seq2[Summary, error]
- func (m *MemoryStore) ListRefs(_ context.Context, prefix string) iter.Seq2[Ref, error]
- func (m *MemoryStore) Open(_ context.Context, id string) (*Session, error)
- func (m *MemoryStore) PutBlob(ctx context.Context, data []byte) (string, error)
- func (m *MemoryStore) Read(ctx context.Context, id string) (*Session, error)
- func (m *MemoryStore) RefLog(_ context.Context, name string) iter.Seq2[RefUpdate, error]
- func (m *MemoryStore) ResolveRef(_ context.Context, name string) (RefTarget, error)
- func (m *MemoryStore) SetHead(ctx context.Context, sessionID, expected, to string) error
- func (m *MemoryStore) SweepBlobs(ctx context.Context, grace time.Duration) (int, error)
- func (m *MemoryStore) UpdateRef(_ context.Context, name string, expected, next RefTarget, reason string) error
- type Normalisation
- type Omit
- type OmitImages
- type OmitOutputs
- type OmittedItem
- type OmittedPart
- type Outcome
- type OutcomeEntry
- func (*OutcomeEntry) EntryType() string
- func (e *OutcomeEntry) MarshalJSON() ([]byte, error)
- func (e *OutcomeEntry) UnmarshalJSON(data []byte) error
- func (e *OutcomeEntry) WithDetails(v any) *OutcomeEntry
- func (e *OutcomeEntry) WithLabel(label string) *OutcomeEntry
- func (e *OutcomeEntry) WithPass(pass bool) *OutcomeEntry
- func (e *OutcomeEntry) WithScore(score float64) *OutcomeEntry
- type QueuedEntry
- type RawEntry
- type Reader
- type RedactedFrom
- type Ref
- type RefMovedError
- type RefStore
- type RefTarget
- type RefUpdate
- type Reference
- type Releaser
- type Remap
- type Remapped
- type ReplacedItem
- type ResponseEntry
- type Result
- type Rewriter
- type Run
- type RunEntry
- type Session
- func Continue(ctx context.Context, store Store, id string, summary openresponses.Item) (*Session, error)
- func ContinueRef(ctx context.Context, st Store, name string, summary openresponses.Item) (*Session, error)
- func Fork(origin *Session, at string, h Header) (*Session, error)
- func ImportMedia(ctx context.Context, st Importer, r io.Reader, media fs.FS, asRecord bool) (*Session, []string, error)
- func New(h Header) *Session
- func Original(ctx context.Context, st Store, s *Session) (*Session, map[string][]string, error)
- func Read(r io.Reader) (*Session, error)
- func ReadForImport(r io.Reader) (*Session, error)
- func SessionFor(ctx context.Context, st Store, name string, h Header) (s *Session, err error)
- func (s *Session) Append(e Entry) (string, error)
- func (s *Session) Branch(id string) error
- func (s *Session) Callees(leaf string) ([]CalleeLink, error)
- func (s *Session) Calls(leaf string) ([]*Call, error)
- func (s *Session) Children(id string) []string
- func (s *Session) Commit(e Entry) (Result, error)
- func (s *Session) Compact(firstKept string, summary openresponses.Item) (*CompactionEntry, error)
- func (s *Session) CompactFrom(first int, summary openresponses.Item) (*CompactionEntry, error)
- func (s *Session) CompactKeeping(kept int, summary openresponses.Item) (*CompactionEntry, error)
- func (s *Session) Context() (Context, error)
- func (s *Session) ContextAt(id string) (Context, error)
- func (s *Session) ContextHash(id string) (string, error)
- func (s *Session) DeclaredFormat() string
- func (s *Session) Dispatches(target string) []*DispatchEntry
- func (s *Session) EndRun(reason, ref string) (*RunEntry, error)
- func (s *Session) Entries() []Entry
- func (s *Session) Entry(id string) (Entry, bool)
- func (s *Session) Extend(e Entry) (Result, error)
- func (s *Session) HeadLabel(expected, to string) (*LabelEntry, error)
- func (s *Session) Header() Header
- func (s *Session) ID() string
- func (s *Session) Judges(leaf string) ([]*LinkEntry, error)
- func (s *Session) Labels() map[string]string
- func (s *Session) Leaf() string
- func (s *Session) LeafMarkBefore(e Entry) *LabelEntry
- func (s *Session) LeafMarkNeeded() *LabelEntry
- func (s *Session) Leaves() []string
- func (s *Session) Len() int
- func (s *Session) MarkLeaf() (*LabelEntry, error)
- func (s *Session) Migrated() (bool, []string)
- func (s *Session) Name() string
- func (s *Session) OpenRun(leaf string) (*Run, error)
- func (s *Session) Path(id string) []Entry
- func (s *Session) PendingCalls(leaf string) ([]*Call, error)
- func (s *Session) PendingQueued(leaf string) ([]*QueuedEntry, error)
- func (s *Session) Prefix(id string) bool
- func (s *Session) Prepare(e Entry) (Result, error)
- func (s *Session) RaiseRefused(format string) error
- func (s *Session) Repeated() []string
- func (s *Session) RequestContext(id string) (Context, error)
- func (s *Session) RequestHashFor(parent string, r *ResponseEntry) (string, error)
- func (s *Session) ResetLeaf()
- func (s *Session) Resolve(id string) (string, bool)
- func (s *Session) Roots() []string
- func (s *Session) Runs(leaf string) ([]*Run, error)
- func (s *Session) SetRequestHash(r *ResponseEntry) error
- func (s *Session) SummarizeBranch(from string, summary openresponses.Item) (*BranchSummaryEntry, error)
- func (s *Session) SupersededBy() string
- func (s *Session) Truncated() *TruncatedLine
- func (s *Session) UnresolvedOf(leaf string) ([]*ConfigEntry, error)
- func (s *Session) Verify(id string) error
- func (s *Session) VerifyLinks(resolve func(id string) (*Session, error)) error
- func (s *Session) VerifyRecords(leaf string) error
- type Settings
- func (s Settings) Apply(c *ConfigEntry) Settings
- func (s Settings) ExtraValue(key string, v any) (bool, error)
- func (s Settings) InstructionsDelta(parts []InstructionPart) *ConfigEntry
- func (s Settings) OmitDelta(want Omit) (*Omit, bool)
- func (s Settings) OmittedDelta(omitted []OmittedPart) []OmittedPart
- func (s Settings) Request(items openresponses.Items) (openresponses.Request, error)
- func (s Settings) RequestJSON(items openresponses.Items) ([]byte, error)
- func (s *Settings) UnmarshalJSON(data []byte) error
- type Span
- type Store
- type Summary
- type Trigger
- type TruncatedLine
- type UnknownEntry
- type UnmetPart
- type VCS
- type Withdrawal
- type Workspace
- type Writer
Constants ¶
const ( TypeItem = "item" TypeResponse = "response" TypeConfig = "config" TypeCompaction = "compaction" TypeBranchSummary = "branch_summary" TypeLabel = "label" TypeInfo = "info" TypeEnv = "env" TypeOutcome = "outcome" TypeLink = "link" TypeCustom = "custom" )
Entry types defined by the format. Any other type of the form "ns:name" is an extension and decodes to UnknownEntry.
const ( RelSubsession = "subsession" RelForkOf = "fork_of" RelContinuedIn = "continued_in" // RelJudgedBy is written into a judged session and names the // session of its judge; see [NewJudgedByLink] and [Judges]. RelJudgedBy = "judged_by" // RelPeer names a callee whose work outlives the call that reached // it and which keeps its own record; see [NewPeerLink]. A callee the // caller started and ends with the call is a [RelSubsession], // wherever it runs: the relation follows the callee's lifetime. RelPeer = "peer" )
Relations a LinkEntry may carry.
const ( OutcomeFeedback = "feedback" OutcomeTest = "test" OutcomeTask = "task" OutcomeToolError = "tool_error" OutcomeEval = "eval" OutcomeCustom = "custom" )
Kinds an OutcomeEntry may carry.
const ( OmitOutputsReason = "outputs" OmitImagesReason = "images" )
OmitOutputsReason and OmitImagesReason are the reasons a Context reports for an item sent with a placeholder: its output, because Omit.Outputs lists its entry, or image parts, which Omit.Images reaches.
const ( MediaInline = "inline" MediaSidecar = "sidecar" )
Media storage modes named by the header.
const ( TypeRun = "run" TypeDispatch = "dispatch" TypeDecision = "decision" TypeQueued = "queued" )
Record entry types added in format 0.2, and queued in 0.3. They are never in context.
const ( ModeSteer = "steer" ModeFollowUp = "followup" )
Modes a QueuedEntry may carry: an input that joins the run in flight, and one that waits for it to end.
const ( RunStart = "start" RunEnd = "end" )
Phases of a RunEntry.
const ( SourceInput = "input" SourceResume = "resume" )
Sources of a run, on its start entry. Both are shapes of the path: a resume's first function call output, decision or dispatch takes up a call that was pending when the run began, whatever messages come before it; an input is everything else, a retry after an error included.
const ( ReasonDone = "done" ReasonStopped = "stopped" ReasonInterrupted = "interrupted" ReasonInputRequired = "input_required" ReasonAborted = "aborted" ReasonError = "error" )
Reasons a run ended, on its end entry. The first five are computable from the run's segment and the path it ends by ComputeReason; ReasonError and ReasonInterrupted are the two a writer adds where the segment cannot show them, and a written one stands over any segment.
const ( VerdictProceed = "proceed" VerdictReject = "reject" VerdictHold = "hold" // VerdictAnswer ends a call that may already have run, one in // flight when the record stopped or one the file cannot say about, // with an output the harness wrote rather than one its tool // returned: the call is not handed to its tool again. By says who // answered and Reason why. VerdictAnswer = "answer" )
Verdicts a DecisionEntry may carry, each defined by what follows the decision on the path: a dispatch, an output carrying the reason, nothing, or an output the harness wrote for a call that may already have run.
const ( ByHuman = "human" ByPolicy = "policy" ByAgent = "agent" )
Deciders a DecisionEntry may name.
const ( WorkspaceLocal = "local" WorkspaceContainer = "container" WorkspaceRemote = "remote" )
Workspace kinds an EnvEntry may name.
const CancelSent = "sent"
CancelSent is the one value of a link's Cancel member: the caller told the callee it no longer wanted the work, which says nothing of whether the work stopped; see NewCancelSentLink.
const Format = "agentsession/0.14"
Format is the session format version this package writes.
const FormatMajor = 0
FormatMajor is the major version of the format this package reads. Any minor version of it is accepted; files are migrated in memory.
const FormatMinor = 14
FormatMinor is the minor version this package writes. A file of an earlier minor is migrated in memory on read: from 0.5 an entry's id is its envelope hash, so every earlier entry is rehashed and keeps its old id in legacy_id. A 0.5 to 0.9 file reads as it stands, since 0.6 to 0.9 only add optional members and elements and 0.10 lets a dispatch elsewhere in the session stand for one on the path, for the answer rule and against the call having never started, which reads a 0.9 file's rebased call differently too; and for two rules 0.8 reads differently: instructions_omitted stays in force until a later config changes it, and a run whose pending call is held after its dispatch ends input_required, not aborted. 0.9 lets instructions_omitted name a run of the list in force by keep, which no 0.8 omitted part could be. 0.10 also marks a file written under 0.9's rules as they stood when they stopped changing. 0.11 accepts a run written resume over a segment that holds nothing, in a 0.9 or 0.10 file too, and adds the of member of an omitted keep, the omit setting, the judged_by link relation and the target member of a link; its writer names an instructions part out of force by its hash. 0.12 adds the peer link relation, the task, context and url members of a link that names a callee in another system, and its cancel member. 0.13 adds the redacted_from header member and the redacted entry member, and makes a redacted session one of its own; a 0.12 file is a 0.13 file without either, and one it holds there is kept as written, as a member this package does not know, and the file is not raised. 0.14 defines the universal root, which a leaf label may name, the host and instance members of an env entry's workspace, an output's failed member, an item's from_link, a reject's withdrawn member, which withdraws the dispatch it directly follows, and omit's outputs and images, which send placeholders; and it accepts a run written resume whose segment holds record entries alone. A 0.13 file is a 0.14 file that holds none of them, and each is read by its form in a file of any minor.
const HashPrefix = "sha256:"
HashPrefix precedes the hexadecimal digest in a request hash.
const LeafLabel = "leaf"
LeafLabel is the reserved label that makes the leaf durable. A label entry carrying it names the branch the next append should continue, not a fixed entry to pin the leaf at: Session.Append keeps the leaf at the label's target rather than moving it to the label entry, and Read resolves the leaf to the newest entry appended under that target after the label, so a branch marked and then written on resumes where it was written to rather than rewinding to the mark. A mark nothing followed resolves to itself. A null label on the same target clears it. The format holds this as a library convention; see the RFC's open questions.
const MaxOriginDepth = 64
MaxOriginDepth bounds the chain of forks OriginDispatches walks: a fork of a fork of a fork, each made at an entry of the prefix the one before carried. The header's parent_session is provenance the format does not validate, so a chain may fail to end where a store is damaged, and a walk that reaches the bound reports it rather than going on.
const MaxRefName = 200
MaxRefName is the longest ref name, in bytes.
const OmitItems = "items"
OmitItems is the reason a Context reports for an item left out because Omit.Items lists its entry.
const OmitOtherModels = "other_models"
OmitOtherModels is the one value of Omit.Reasoning the format defines: a request leaves out each item entry holding a reasoning item that carries a response and was written while a model other than the request's was in force. It is also the reason a Context reports for an item left out by that rule.
const PartSeparator = "\n\n"
PartSeparator joins the instruction parts into the instructions string. The rule is the format's, so a writer that records parts and a reader that rebuilds the string produce the same bytes and the request hash verifies.
const Payload = "openresponses/" + openresponses.SpecVersion
Payload is the payload profile this package writes: Open Responses items at the specification version openresponses targets.
Variables ¶
var AllRecords = []string{TypeRun, TypeDispatch, TypeDecision}
AllRecords are the three record entry types a harness that runs the loop itself can promise: run, dispatch and decision. A harness that also accepts inputs while a run is in flight adds TypeQueued, which promises that every such input is recorded as a queued entry before it is acted on, so a reader may take the absence of one as nothing having been queued.
var ErrAnswerNotDispatched = errors.New("agentsession: answer for a call the record shows never started")
ErrAnswerNotDispatched is returned when an answer decision is appended for a call with no dispatch in a session whose header promises dispatch records: the record says that call never started, so it did not run, and a writer that ends it without running it writes a reject.
var ErrBadConvergence = errors.New("agentsession: bad convergence reference")
ErrBadConvergence is returned for an entry whose Parents break the format's rules: a reference naming no entry, the same entry named twice, a reference to the entry's own parent, or a reference into this session naming an entry that does not already exist.
var ErrBadFromLink = errors.New("agentsession: from_link names no peer link before the item")
ErrBadFromLink is returned by Session.VerifyRecords for an item whose from_link names no peer link before it on its path, a cancel notice included, since a cancel notice names no callee.
var ErrBadID = errors.New("agentsession: entry id does not match its hash")
ErrBadID is returned by Append for an entry whose ID was set by the caller and does not match the hash the format defines, and by Read for a line whose id does not verify.
var ErrBadNormalisation = errors.New("agentsession: normalised entry is malformed")
ErrBadNormalisation is returned for a Normalisation a caller set that the format cannot carry: no pointer, or not exactly one of Was and Raw.
var ErrBadRedacted = errors.New("agentsession: malformed redacted")
ErrBadRedacted is returned by SetRedacted for spans not in the form the format gives an entry's redacted member: each position at least 1, each range's From below its To, in ascending order and not overlapping.
var ErrBadTarget = errors.New("agentsession: target does not name the call")
ErrBadTarget is returned when a decision or dispatch is appended whose target is not the entry of a function call on the path with the decision's call ID: a target names the call, and its call ID must agree.
var ErrBadWithdrawal = errors.New("agentsession: withdrawal does not follow a dispatch of its call")
ErrBadWithdrawal is returned when a reject carrying withdrawn is appended anywhere but directly after a dispatch of its call, before any other decision, dispatch or output of it: a withdrawal withdraws the hand-off it follows and is written nowhere else. A reader reads one found elsewhere as a reject without the member.
var ErrBaseRule = errors.New("agentsession: entry breaks the base rule")
ErrBaseRule is returned for an entry that breaks the rule a base imposes on a session: the entries before the base are the path to it, one root and each the child of the one before, and every entry after it hangs from the base or from an entry after it, never from null, since a session with a base has one root, and never from the prefix above the base, since the prefix is another session's record and branching above the base is a new session with a lower base. Read and Scan hold a file's lines to it and Append the entry it is given, so a file that reads is one Append can continue. An error reporting it is also ErrNoEntry under errors.Is, which Append returned for it before this sentinel existed.
var ErrBlobMismatch = errors.New("agentsession: a sidecar blob does not hash to its name")
ErrBlobMismatch is returned by an import whose sidecar directory holds a blob whose bytes do not hash to its name: the blob is not the one the items name, and the import is refused, as one whose line fails its hash is.
var ErrCallAnswered = errors.New("agentsession: call was answered")
ErrCallAnswered is returned when a dispatch or a decision is appended for a call that an answer decision on the path already ended: what follows an answer is the call's output and nothing else.
var ErrCallCompleted = errors.New("agentsession: call has its output")
ErrCallCompleted is returned when an answer, a reject or a dispatch is appended for a call whose output is already on the path: the output ends the call, and an answer or reject is for a call that has none.
var ErrCallIDEmpty = errors.New("agentsession: function call has no call ID")
ErrCallIDEmpty is returned when a function call is appended with no call ID, and by Session.VerifyRecords for a session that holds one: nothing could name the call, not its output, a decision or a pending list.
var ErrCallIDRepeated = errors.New("agentsession: call ID repeated")
ErrCallIDRepeated is returned when a function call is appended whose call ID another function call in the session has, on any branch, and by Session.VerifyRecords for a session that holds two: a call ID names one call in a session.
var ErrCallRejected = errors.New("agentsession: call was rejected")
ErrCallRejected is returned when a dispatch or a decision is appended for a call that a decision on the path already rejected: what follows a reject is the call's refusal output and nothing else.
var ErrCalleeLink = errors.New("agentsession: callee link is not one the format allows")
verifyTargets reports a decision or dispatch on the path whose target is not a function call before it on the path, or names one with another call ID. ErrCalleeLink is returned by Session.VerifyRecords for a link that carries a member 0.12 defines and breaks the rules that come with it: a peer link with a url and no call, or a cancel notice whose target is not a subsession or peer link of its call and relation before it on the path. Session.Append and Session.Prepare refuse such a link, with a peer link naming neither a url nor a session, when it is written; the decoder reads one as written, so a stored one is reported here. A link carrying none of those members, which an earlier file may hold under an extension's meaning, is not held to them.
var ErrHashMismatch = errors.New("agentsession: request hash mismatch")
ErrHashMismatch is returned by Session.Verify when the rebuilt request does not hash to the recorded value.
var ErrHeadMoved = errors.New("agentsession: head is not the expected entry")
ErrHeadMoved is returned by HeadStore.SetHead when the session's head is not the expected entry, which is how a writer learns that another moved it.
var ErrHeadTarget = errors.New("agentsession: the head never rests on a leaf label")
ErrHeadTarget is returned by HeadStore.SetHead for a target the head never rests on: a leaf label.
var ErrInstructionsJoin = errors.New("agentsession: rewritten instructions cannot be shown to be the rewritten parts joined")
ErrInstructionsJoin is returned by RewriteStrings for an entry whose instructions sit beside parts it cannot resolve, a part named by hash or by keep whose text is in an entry the Rewriter was not given, when f would change the instructions: the rewritten string could no longer be shown to be the rewritten parts joined, which the format requires it to stay, so the rewrite fails closed.
var ErrKeyCollision = errors.New("agentsession: a rewrite makes two member names one")
ErrKeyCollision is returned by RewriteStrings for an entry in which the rewrite gives two member names that hold data the same name, such as two paths of an env entry's files made one, or an extension entry's member a name its envelope holds, which the format forbids a redaction to keep.
var ErrLinkMismatch = errors.New("agentsession: subsession link disagrees with its target's header")
ErrLinkMismatch is returned by Session.VerifyLinks for a subsession link whose target's header names another session as its parent, or another call as the one that spawned it. The link and the header are two records of one spawn, written by the parent and by the child, and RFC 0001 has them agree. The disagreement is what a line moved to a fork after a call ID collision leaves behind when its links were not pointed at the subsessions re-derived under the fork's ID: two stores derive one ID for the subsession of one call, so the link names the other store's subagent, and only that session's header says so.
var ErrMediaDiffers = errors.New("agentsession: a fork's media differs from that of the session holding its base")
ErrMediaDiffers is returned by an import, an exchange or a create of a fork whose media differs from that of the session whose own entries include its base, which RFC 0002 refuses: the prefix was written in that session's form, and a projection carries media in one.
var ErrNoBlob = errors.New("agentsession: no such blob")
ErrNoBlob is returned by BlobReader.Blob for a blob the store does not hold.
var ErrNoBlobWriter = errors.New("agentsession: the store cannot hold sidecar media, so it takes no sidecar session")
ErrNoBlobWriter is returned for a sidecar session given to a store that cannot hold its blobs, which RFC 0002 has it refuse.
var ErrNoEntry = errors.New("agentsession: no such entry")
ErrNoEntry is returned when an entry ID is not in the session.
var ErrNoHash = errors.New("agentsession: no request hash recorded")
ErrNoHash is returned by Session.Verify for a response entry that recorded no request hash. The response is not verified and not mismatched: there was nothing to check. A writer records no hash when it cannot stand behind one, which is what a layer that edits the request outside the transcript leaves behind, so a caller that accepts unverified requests opts in with errors.Is rather than by reading err == nil.
var ErrNoPlace = errors.New("agentsession: a member naming one entry has nothing in the place of the entry left out")
ErrNoPlace is returned by RemapRefs when a Remap says an entry was left out with nothing in its place, an empty ID, for a member that names one predecessor and so must name what stands in the left-out entry's place (RFC 0001, Leaving entries out): a target, a from, a first_kept, images' before or an omitted part's of. A leaf label's target is not refused: it names the universal root, as parent does. A cancel notice's target is refused too, and a writer leaves the notice out with the link it names, since nothing standing in a link's place is a link.
var ErrNoRaise = fmt.Errorf("%w: the file holds redacted_from or redacted from before 0.13 and is not raised", ErrUnsupportedFormat)
ErrNoRaise is returned for a file a writer may not raise to the format it writes, and so for an append to its session: one whose header names a minor before 0.13 and holds a redacted_from member, or one an entry of which holds a redacted member under such a header, in any form, which the raise would give a meaning it was not written with. It is an ErrUnsupportedFormat, since this writer cannot append under the header's own format either.
var ErrNoRef = errors.New("agentsession: no such ref")
ErrNoRef is returned by ResolveRef when the store holds no ref of the name.
var ErrNoRefs = errors.New("agentsession: store does not keep refs")
ErrNoRefs is returned by the helpers that need refs when the store does not keep them.
var ErrNoSession = errors.New("agentsession: no such session")
ErrNoSession is returned when a session ID is not in the store.
var ErrNotGit = errors.New("agentsession: not a git checkout")
ErrNotGit is returned by GitRevision when dir is not inside a git checkout.
var ErrNotRedacted = errors.New("agentsession: not redacted")
ErrNotRedacted is returned by a store set to hold only redacted entries for what was not redacted: a session whose header does not carry redacted, at its create, its import, or a push or fetch it receives, and an entry that carries no redacted member (RedactedOf false), however it arrives, an append, an import, a push or a fetch, a prefix entry included. A fork of a redacted session and an entry a writer appended to one natively are among them, since neither was redacted. Original returns it for a session that is not redacted.
var ErrOmitDivergence = errors.New("agentsession: the recorded hash is the request without the omit setting applied")
ErrOmitDivergence is wrapped, beside ErrHashMismatch, by the error that Session.Verify returns when the response's recorded hash is the request built with the items the omit in force leaves out: the writer sent, or hashed, what the record says a request leaves out. It is a divergence between the record and its own rule, and not a response that recorded no hash.
var ErrOriginChain = errors.New("agentsession: the chain of forks does not end")
ErrOriginChain is returned by OriginDispatches when the sessions a fork was made from do not end: parent_session names a session already on the walk, or the walk reaches MaxOriginDepth.
var ErrReadOnly = errors.New("agentsession: store is read-only")
ErrReadOnly is returned by a store opened read-only when a caller tries to write: such a store takes no hold on the sessions it opens, so it must not append to them either. Reading a session while a harness writes it is what it is for.
var ErrReasonMismatch = errors.New("agentsession: run end disagrees with its segment")
ErrReasonMismatch is returned by Run.Verify when a run's written end reason or pending list disagrees with its segment.
var ErrRecordMissing = errors.New("agentsession: promised record entry missing")
ErrRecordMissing is returned by Session.VerifyRecords when the header promises a record entry type and the path lacks one where the event plainly happened.
var ErrRedactedFrom = errors.New("agentsession: malformed redacted_from")
ErrRedactedFrom is returned for a header whose redacted_from the format forbids: one on a session that is not redacted, one that names the session itself as its original, or, built in memory, one that names no original.
var ErrRefMoved = errors.New("agentsession: ref moved")
ErrRefMoved is returned by UpdateRef when the ref does not hold the expected target. The error wraps it as a RefMovedError, which carries the target the ref holds.
var ErrRefName = errors.New("agentsession: invalid ref name")
ErrRefName is returned for a name or prefix RFC 0002 does not allow, and for a name that conflicts with a ref the store holds.
var ErrRejectDispatched = errors.New("agentsession: reject for a call that was dispatched")
ErrRejectDispatched is returned when a reject is appended for a call that has a dispatch on the path: a reject says the call did not run, and one that may have run is ended by an answer.
var ErrReservedMember = errors.New("agentsession: body carries a reserved envelope name")
ErrReservedMember is returned for a body carrying a top-level member by one of the envelope's names.
var ErrScanMigrated = errors.New("agentsession: a file before 0.5 has no hashed ids to scan; read it")
ErrScanMigrated is returned by Scan for a file of a minor before 0.5, whose entries carry no hashes to verify: Read migrates one.
var ErrSessionExists = errors.New("agentsession: session already exists")
ErrSessionExists is returned when Create is given an ID already in the store.
var ErrSessionLocked = errors.New("agentsession: session is open in another process")
ErrSessionLocked is returned by a store whose session is held by another process. It is the one sentinel for that condition: each store wraps it, so a host written against the Store interface can tell "another process has this session", which means leave the conversation alone, from a store that is broken, which means the daemon is unhealthy, without knowing which store it was given.
var ErrSourceMismatch = errors.New("agentsession: run source disagrees with its segment")
ErrSourceMismatch is returned by Session.VerifyRecords for a run start whose source is not the shape of its segment; see ComputeSource. A run written resume over a segment that holds record entries at most is not one, as Run.Empty says.
var ErrStaleOriginal = errors.New("agentsession: the store's copy of the original is behind the version redacted")
ErrStaleOriginal is returned by Original when a redacted entry names a position past the entries the store holds of the original: a copy, a mirror say, that has not reached the version redacted. Fetching the original and asking again may answer.
var ErrSuperseded = errors.New("agentsession: session was continued in a successor")
ErrSuperseded is returned by Continue for a session a continued_in link has already retired, and by every store of this module for an append of a second continued_in link naming another successor, which would fork the chain (RFC 0002, Refs and continuation). A store may also refuse any append to such a session with it; none of this module's does.
var ErrSyntheticMarker = errors.New("agentsession: the file's head is named by a projection's synthetic leaf marker, which this store cannot keep")
ErrSyntheticMarker is returned by ReadForImport for a file holding a projection's synthetic leaf marker: a label carrying synthetic, which RFC 0002 writes only where the resume rule over the lines before it would not name the session's head. A store that keeps a session's head only in its entries cannot hold that head without adding an entry, which would change the session, so it refuses the file.
var ErrUnresolvedMigration = errors.New("agentsession: migrated session holds entries that could not be rewritten")
ErrUnresolvedMigration is returned by Write for a session migrated from an earlier minor version that holds an entry the migration could not rewrite.
var ErrUnsupportedFormat = errors.New("agentsession: unsupported format")
ErrUnsupportedFormat is returned when a header names a format this package cannot read.
Functions ¶
func CanonicalRequest ¶ added in v0.0.19
func CanonicalRequest(req openresponses.Request) (openresponses.Request, error)
CanonicalRequest returns req as its canonical JSON (RFC 8785) decodes: every member inside opaque JSON, such as a tool's parameter schema, in canonical order and spelling. A request rebuilt from a session equals the one sent under canonical JSON, not in bytes: a cas store holds bodies in canonical form, while a jsonl file gives members as they were written. A renderer that turns a request into tokens passes the live request and the rebuilt one through CanonicalRequest alike, and then renders the same bytes from either. It does not change the request hash.
func CanonicalTime ¶ added in v0.0.8
CanonicalTime renders t in the one form the format admits for ts: UTC, uppercase T and Z, a fractional part only when non-zero, with no trailing zeros and at most nine digits. It is what Go's RFC3339Nano layout produces for a UTC time, so a time.Time round trips unchanged, which is the property the rule was chosen for.
func CheckCreate ¶ added in v0.0.27
CheckCreate is the refusal a store makes of a create for what the redaction rules forbid: a redacted_from RFC 0001 calls malformed (ErrRedactedFrom), one on a session that is not redacted, whether typed or held in Extra as a header decoded without a format holds it, one naming no session, or one naming the session itself, as Header.Validate refuses it on a read; and, in a store that holds only redacted entries, a header that does not carry redacted (ErrNotRedacted). A store calls it before it creates anything; nil means it may. A header whose ID the store fills names no session yet, so cannot name itself. A redacted session is created as any other is.
func CheckHoldsRedacted ¶ added in v0.0.27
CheckHoldsRedacted reads every session st lists and refuses, with ErrNotRedacted, the first whose header does not carry redacted or that holds an entry without it, as CheckRedacted does: what a store set to hold only redacted entries does at its open, so it is not set that way over what it already holds. A session it cannot read, or a listing error, refuses too.
func CheckImportMedia ¶ added in v0.0.27
CheckImportMedia is the media check RFC 0002's import makes, for a store that has found origin, the session holding the base, or nil when it holds none: a fork whose media differs from origin's is refused with ErrMediaDiffers. A store finds origin by following parent_session up to the session that owns the base, else takes a session that owns it, else one that holds it on its prefix, whose media is its own origin's, as a create forks from it; and only among the sessions ReadableIn admits.
func CheckRedacted ¶ added in v0.0.27
CheckRedacted is the refusal a store that holds only redacted entries makes, with ErrNotRedacted, of a session that would bring it anything not redacted: a header that does not carry redacted, or an entry, the prefix's included, that carries no redacted member. A store calls it on what an import, a push or a fetch delivers, and, with the session's header and the one entry, on an append.
func CheckRedactedIn ¶ added in v0.0.31
CheckRedactedIn is CheckRedacted for entries about to join s, whose own entries a head move's target may name.
func ClearRedacted ¶ added in v0.0.27
ClearRedacted removes e's redacted member, in whatever form it held it, as a writer does to an entry it appends natively from one it took from a redacted session. Like SetRedacted, it is for an entry not yet appended.
func ComputeReason ¶ added in v0.0.5
ComputeReason recomputes a run's end reason from its segment and the root-first path the segment ends, as the format defines it: the first of error, input_required, aborted, done and stopped whose shape they match, with aborted again as the value for anything left. A nil path means the segment stands for the path. It never returns ReasonInterrupted, and returns ReasonError only for a response that carries an error; both are values a writer adds where the segment cannot show them.
A pending call is one of the run's calls, as Run.Calls has them, with no output: a call made before the segment counts once the segment holds a decision, dispatch or output for it, so a resume that holds such a call before any model call ends input_required.
The stopped step reads the path because a run that answers a call and ends without calling the model again, which is what a resume whose tool asks to terminate and a refusal both are, holds no response of its own: the response that made the calls is on the path before the segment.
func ComputeSource ¶ added in v0.0.17
ComputeSource returns the source a run's start names, by the format's rule: SourceResume when the segment's first function call output, decision or dispatch takes up a call that was on the path with no output when the run began, whatever messages come before it, and SourceInput otherwise, a run that takes up nothing included. A run built by hand with no Path has nothing before its segment, so it is an input.
It is the shape the segment shows, which is not always what the writer meant: a run started to take up a call and ended before it did has a segment that takes nothing up, which computes as an input. The format accepts such a run written SourceResume as it is written, as Run.Empty says, and Session.VerifyRecords does not report it; a writer still computes the source it writes at start from what it knows then, and need not change.
func EntryHashes ¶ added in v0.0.8
EntryHashes computes an entry's two hashes from its encoded form, a JSON object with the envelope members in it: the content hash over the body, the members outside the envelope, and the id over the envelope object — type, parent, parents when present and non-empty, ts, and content holding the content hash. The id member in data, if any, is ignored; ts is taken as the string it is. Both are "sha256:" plus lowercase hex over the canonical bytes.
func GateEntry ¶ added in v0.0.27
GateEntry reads e, an entry decoded from its line alone, as UnmarshalEntry decodes it, under format, the format its file's header names: before 0.13 the redacted member is not defined, so one in any form becomes a member this package does not know, kept as written, and RedactedOf reports e as not redacted. The entry is then marked as holding such a member, so a session it joins, by Session.Commit or any other path, may not be written at 0.13 or later (Session.RaiseRefused) and Session.Append refuses it. Read, RawEntry.Decode and Session.Extend apply it themselves; a store that decodes a line and commits it to a session it read, as a log reader or a follower does, calls GateEntry with the format of the header it read the line under. From 0.13 on it changes nothing.
func GitRevision ¶
GitRevision returns the commit HEAD points at for the checkout containing dir, reading .git directly so no git binary is needed. It follows symbolic refs through loose refs and packed-refs and handles worktrees and submodules whose .git is a file.
func HashFile ¶
HashFile returns the content hash of a file in the form the env entry uses: "sha256:" followed by lowercase hex.
func HashFiles ¶
HashFiles hashes each path, keyed as given. Paths are hashed as they are; callers that want keys relative to a directory pass them that way and resolve against dir.
func HashRequestJSON ¶
HashRequestJSON computes the request hash of an already-encoded request. Member order and whitespace in data do not matter.
func HashText ¶ added in v0.0.6
HashText returns the hash of a string in the format's notation, which is HashBytes over its UTF-8 bytes: HashPrefix and the lowercase hexadecimal SHA-256. It is how a config delta names the text of an instructions part it does not repeat, so a reader can tell the part it already has from one that changed.
func HeadMove ¶ added in v0.0.31
HeadMove reports whether e is the record of a head move and nothing more (RFC 0002, Head): a leaf label whose target names an entry holds reports, or the universal root (an empty target, null on the wire) when holds reports "" held, and which carries no other member at any depth, no parents, legacy_id, normalised or member of its own. It carries no content to have been redacted, so a store set to hold only redacted entries takes it, its check being of what was run over an entry's content (RFC 0002, Redacted sessions); CheckRedacted passes it. A target of free text is content, and so is a member beside it.
func HeaderIdent ¶ added in v0.0.21
HeaderIdent names a session's incarnation: the hash of its header without `format`, which is all a writer's rewrite of a session changes, as a follower tells one session from another created again under its ID. A store records it with each ref it sets and compares it at resolution, so a ref to a deleted session never reaches a session created later under that ID. It is the store's, and no RefTarget carries it.
func InContext ¶
InContext reports whether an entry contributes an item to the model context: item, branch_summary and compaction entries do. Whether a compaction is actually selected depends on the path; see Session.ContextAt.
func IsExtension ¶
IsExtension reports whether typ is a namespaced extension type.
func JoinInstructions ¶ added in v0.0.6
func JoinInstructions(parts []InstructionPart) string
JoinInstructions returns the instructions the parts compose: their texts joined with PartSeparator, in order.
func MarshalEntry ¶
MarshalEntry encodes one entry as a single JSON line without the trailing newline. The envelope members come first in a fixed order, then the type's members, then any unknown members in key order.
func NewBaseRuleError ¶ added in v0.0.20
NewBaseRuleError returns an error reporting that an entry breaks the base rule, with detail saying how: ErrBaseRule and ErrNoEntry both under errors.Is, as the session reports it. A store that meets the rule before the session does, as one recording a reset leaf on a session with a base, reports it the same way.
func NewEntryID ¶
func NewEntryID() string
NewEntryID returns a short random entry ID: eight hexadecimal characters. Session.Append regenerates on the rare collision within a file, so callers need not check.
func NewSessionID ¶
func NewSessionID() string
NewSessionID returns a UUIDv7 in its canonical text form. The leading 48 bits are the current Unix time in milliseconds, so IDs sort by creation time; the rest is random.
func OriginDispatches ¶ added in v0.0.20
func OriginDispatches(ctx context.Context, r Reader, s *Session, callEntry string) (*Session, []*DispatchEntry, error)
OriginDispatches returns the dispatches for the call whose function call is the entry callEntry, and the session holding them. They are s's own, as Session.Dispatches has them, when s holds any. A call in a fork's prefix has its dispatches, if any, in the session the fork was made from, which the fork's file does not carry: for such a call with none in s, the session the header's ParentSession names is read through r and asked the same, and so up a chain of forks, each made at an entry on the prefix the one before carried, to MaxOriginDepth. A dispatch found there is read as one off the path, which a rebase above a dispatch leaves: the call may have run, under that dispatch's key and with the arguments it handed over, and its output, when the origin holds one, is on that origin's branch. It returns nil, nil and no error when no session on the walk holds one, when the walk reaches a session that names no parent, when r does not hold the session named (ErrNoSession), or when r is nil, which reads s alone. Any other error reading an origin is returned, wrapping the store's, and so is ErrOriginChain for a chain that does not end.
The store is passed, not read behind the caller's back: an Open or a Read of a fork does not go and read other sessions, so a reader that wants a prefix call's dispatches asks for them here, with the store it holds. Call.State reads a prefix call with no dispatch on its path as CallUnknown either way; what this adds is the dispatch itself.
func ParseCanonicalTime ¶ added in v0.0.8
ParseCanonicalTime parses s and reports whether it is spelled in the canonical form: a spelling the format admits renders back to itself.
func ParseFormat ¶
ParseFormat splits a format string such as "agentsession/0.1" into its major and minor versions.
func ReadSidecarBlob ¶ added in v0.0.31
ReadSidecarBlob reads the blob named hash from a session's sidecar directory, where RFC 0001 names it by its hex digest alone, and verifies it against the hash. A blob the directory lacks is fs.ErrNotExist; one whose bytes do not hash to its name is ErrBlobMismatch.
func ReadableIn ¶ added in v0.0.31
ReadableIn returns the filter WithReadable put in ctx, or one that admits every session.
func RecordResponse ¶
func RecordResponse(ctx context.Context, store Store, sessionID string, req openresponses.Request, resp *openresponses.Response, latency time.Duration) (string, error)
RecordResponse writes one model call to a session: every output item of resp as an item entry carrying resp.ID, then the response entry with the response's model, status, usage, incomplete details, error, the hash of req and the latency. req must be the request as it was sent, so the recorded hash is the one a reader rebuilds from the path; pass a zero latency to leave latency_ms out. It returns the ID of the response entry.
The caller records the user items and function call outputs that precede the request before calling this, as the format requires.
func RedactedSessionID ¶ added in v0.0.27
RedactedSessionID derives the session ID the format recommends for a redacted session whose redacted_from writes a profile: a UUIDv5 under the nil namespace over the name ["redacted","<original>","<profile>"], a JSON array in the canonical form the entry hash uses, original being the original's session ID. An array cannot be read two ways, as a slash-joined name can when an ID holds a slash, and it is not a subsession's name, which starts with a session ID. One original under one profile then has one redacted session, whose ID stays as it grows, so a redaction repeated over a store finds the session it made before and appends only what the original added since. A profile names one set of rules, since two under one name write into one session. A string that is not UTF-8 is encoded as the JSON encoder does, its invalid bytes as U+FFFD.
func Redactions ¶ added in v0.0.27
Redactions lists the sessions st holds that were redacted from the session id: those whose header's redacted_from names it, newest first, as Store.List gives them. Nothing is written into an original about what was made from it, so this is how its holder finds them. It reads every header the store lists; a header from before 0.13 holds no redacted_from this package reads, and is not one.
func RefConflict ¶ added in v0.0.21
RefConflict reports whether creating name beside the refs in existing breaks RFC 0002's rules on names: name is an existing ref's name, or a prefix of one, or has one as a prefix, at a segment boundary (a ref under a ref), or some prefix of it at a segment boundary differs only in ASCII case from the same-length prefix of an existing name. It returns nil for a name that is itself in existing and spelled the same, which creates nothing new.
func RemapHeaderRefs ¶ added in v0.0.27
RemapHeaderRefs is RemapRefs for a header: base names the entry the session continues from, which the file holds as the last line of its prefix and so names in this file, and parent_session names a session. redacted_from names the original a redacted session was made from, which a redaction does not re-point, so it stays as written.
func RequestHash ¶
func RequestHash(req openresponses.Request) (string, error)
RequestHash computes the request hash the format defines: the request serialised with the JSON Canonicalization Scheme (RFC 8785) and digested with SHA-256, rendered as "sha256:" plus lowercase hex. The input must be the request as it was sent, passthrough members included, because those reached the model; in a sidecar session, the request with each sidecar: URL as the item records it, which is the form a reader rebuilds, and not with the bytes the writer resolved it to before sending.
func ResolveCurrent ¶ added in v0.0.21
ResolveCurrent returns the session that is current for name: the ref's target, followed along continued_in links to the end of the chain. moved reports that it followed at least one, which says the ref is behind, as after a writer that continued the session and stopped before it moved the ref. It stops at a cycle with an error. The store's ResolveRef stays literal; this is the caller's choice.
It reads sessions through Reader when the store is one, and otherwise with Open.
func SameWorkspace ¶ added in v0.0.10
SameWorkspace reports whether two workspace members are the same under the format's substitution rule: compared member by member in their canonical form, the ones the format does not define included, and an absent one equal only to another absent one. An empty kind or ref is the same as an absent one, as the format says. An env entry whose workspace is not the same as the one in force before it on the path is a substitution.
func SessionBlobs ¶ added in v0.0.31
SessionBlobs returns the hashes of the sidecar blobs a session's entries name, its prefix included, each once.
func SetRedacted ¶ added in v0.0.27
SetRedacted marks e as a redacted entry made from the entries of the original that spans name, as a redactor marks every entry it writes: spans in the form ErrBadRedacted says, each a position (From == To) or a range, by position in the original's file order counting from 1. Nil or empty spans mark an entry the redaction added. It replaces any redacted member e held, in whatever form, and refuses spans not in the form with ErrBadRedacted, leaving e as it was. It changes e, so it is for an entry not yet appended, whose hashes the session computes when it commits it. A redacted response also takes the request_hash of its request in the redacted session, not the original's, which Session.SetRequestHash sets.
func SidecarBlobs ¶ added in v0.0.31
SidecarBlobs returns the hashes of the sidecar blobs an entry's items name, each once, in the order they first appear.
func SubsessionID ¶ added in v0.0.5
SubsessionID derives the session ID the format recommends for a subsession: a UUIDv5 under the nil namespace over "<parent session id>/<call_id>", so a reader can compute the child's ID from the parent's link or function call alone, before or after the child exists. A second child for the same call appends a new root to the existing child session rather than minting a second ID.
func ToolName ¶
func ToolName(t openresponses.Tool) string
ToolName returns the name of a tool: the function name for a function tool, the "name" member of an extension tool when it has one, else "".
func ValidHash ¶ added in v0.0.8
ValidHash reports whether s is a hash in the format's notation: "sha256:" followed by exactly 64 lowercase hexadecimal characters. Anything a store puts on a filesystem as a hash is checked with it.
func ValidRefName ¶ added in v0.0.21
ValidRefName reports whether name is a ref name by RFC 0002's rules, which are about the name alone: segments of [A-Za-z0-9._-] joined by "/", none empty, "." or "..", ending in a dot or naming a Windows device, at most MaxRefName bytes. A store checks what depends on the refs it holds with RefConflict.
func ValidRefPrefix ¶ added in v0.0.21
ValidRefPrefix reports whether prefix can begin a ref name: the empty prefix, or characters a name allows and "/". It need not end at a segment boundary.
func WithReadable ¶ added in v0.0.31
WithReadable returns a context that carries readable, which reports whether the caller a store works for may read a session, as a server that authorizes per caller knows (RFC 0003, Security considerations). A store that finds the session a fork continues from, at a create, an import or a push, considers only sessions readable admits: one it does not admit is as one the store does not hold, so a refusal, or a header the store fills from the origin, says nothing of a session the caller may not read. A store asks readable holding none of its own locks, so readable may read the store it guards, as an authorizer that finds a session's owner in its header does. A context without it admits every session.
func Write ¶
Write encodes the session as JSONL: the header, then every entry in file order, one per line, each in its canonical form. Preservation is of members and not bytes, and the hashes are over canonical forms, so a canonical line is what a reader verifies most directly. A session migrated from an earlier minor version that holds an entry the migration could not rewrite is refused, since the format forbids re-emitting such a file as 0.5.
Types ¶
type BlobReader ¶ added in v0.0.31
BlobReader is implemented by a store that serves the sidecar blobs it holds: Blob returns a blob's bytes by its hash, and an error wrapping ErrNoBlob for one it does not hold.
type BlobSweeper ¶ added in v0.0.31
BlobSweeper is implemented by a store whose blobs RFC 0002's sweep collects (Deletion and retention): SweepBlobs removes each blob that no entry of any session the store holds names, prefixes included, and that was stored, or stored again, longer than grace ago, and returns how many it removed. A blob is retained while a session names it, and grace spares one a write is about to name, as an upload awaits the append, import or push that names it. A sweep never removes a blob that a write it races names: MemoryStore and the sqlite store sweep in one step no write interleaves with, and the jsonl store's append and import restart the grace of each blob they name before they land, and its sweep looks at a blob's age again under a guard the restart takes. The library sweeps only when called; RFC 0003's server calls it for the store it serves. A store whose blobs are objects among others, as cas's are, collects them in its own sweep instead.
type BlobWriter ¶ added in v0.0.31
BlobWriter is implemented by a store that holds sidecar media, as RFC 0002's Media section requires of a store that holds a sidecar session: PutBlob stores a blob by its bytes under its hash, the SHA-256 of the bytes in the format's notation, and returns the hash. An item names the blob with a sidecar: URL carrying the hash. Storing a blob the store holds already changes nothing.
A store that cannot hold blobs refuses to create or import a session whose media is sidecar, with ErrNoBlobWriter.
type BranchSummaryEntry ¶
type BranchSummaryEntry struct {
EntryBase `json:"-"`
From string `json:"from"`
Summary openresponses.Item `json:"summary"`
Usage *openresponses.Usage `json:"usage,omitempty"`
}
BranchSummaryEntry carries context across a branch switch. Its parent is where the new branch continues; From is the leaf that was left. It is in context through its summary.
func (*BranchSummaryEntry) EntryType ¶
func (*BranchSummaryEntry) EntryType() string
EntryType returns "branch_summary".
func (*BranchSummaryEntry) MarshalJSON ¶
func (e *BranchSummaryEntry) MarshalJSON() ([]byte, error)
MarshalJSON emits the entry as one JSON object.
func (*BranchSummaryEntry) UnmarshalJSON ¶
func (e *BranchSummaryEntry) UnmarshalJSON(data []byte) error
UnmarshalJSON decodes the entry.
type Call ¶ added in v0.0.5
type Call struct {
// Entry is the item entry holding the function call.
Entry *ItemEntry
// Call is the function call item.
Call *openresponses.FunctionCall
// Decisions are the decisions on the call, in path order.
Decisions []*DecisionEntry
// Dispatch is the first dispatch entry, or nil when none is on the
// path.
Dispatch *DispatchEntry
// Dispatches are the dispatch entries, in path order: one for each
// time the call was handed to its tool, so a call run again after a
// restart has two. Dispatch is the first of them.
Dispatches []*DispatchEntry
// Output is the item entry holding the function call output, or nil
// when the call is pending.
Output *ItemEntry
// Withdrawn are the hand-offs a writer withdrew before the tool ran,
// in path order: each a dispatch and the reject carrying withdrawn
// that directly follows it among the call's entries. The format reads
// such a hand-off as never made and the reject as no decision, so
// neither is in Dispatches or Decisions, and the call is what it was
// before the dispatch.
Withdrawn []Withdrawal
// contains filtered or unexported fields
}
Call is one function call on a path and everything that happened to it there: the decisions made about it, its dispatches and its output.
func Calls ¶ added in v0.0.5
Calls collects the function calls on a root-first path, in the order their items appear, with the decisions, dispatch and output the path holds for each. A decision or dispatch belongs to the call its target names, an output to the latest call before it with its call ID, as the format has it; one whose call is not on the path is ignored.
func (*Call) Answered ¶ added in v0.0.11
Answered reports whether a decision ended the call with an output the harness wrote rather than one its tool returned: a call that may have run, answered without being handed to its tool again.
func (*Call) Args ¶ added in v0.0.5
Args returns the arguments the tool runs with: those of the last decision that rewrote them, else the call's own. An answer's args are passed over: no tool ran with them, the format says they name nothing, and reading them here would show an output the harness wrote as a rewrite of what the tool runs with. They are read from the decision itself.
func (*Call) DispatchedArgs ¶ added in v0.0.11
DispatchedArgs returns the arguments the call's last dispatch handed its tool: those of the last decision before it that rewrote them, else the call's own. It is "" when the call has no dispatch. A replay rule deciding whether that hand-off may run again reads these beside Call.IdempotencyKey; Call.Args are the arguments a new hand-off would run with, which a later decision may have changed.
func (*Call) Failed ¶ added in v0.0.31
Failed reports whether the call's output is marked failed: its tool reported failure, or the harness gave the call up, and the output describes it. A failed call is completed all the same; a pending call has not failed.
func (*Call) From ¶ added in v0.0.8
From returns the predecessors the call's output converges: for a call a subagent answered, the leaf of the child session the answer was taken from, which the format says the output entry SHOULD name and which is the first moment the parent knows it. It is nil for a pending call, and for an output whose writer recorded no provenance.
A LinkEntry names the child session and is written when the call is dispatched, before the child has a point to name; this is the other half of that round trip. Without it, a child that branched leaves no record of which of its leaves answered, and a projection has to choose.
func (*Call) Held ¶ added in v0.0.5
Held reports whether the call is waiting on an answer: its latest decision is a hold and no dispatch follows it. A hold after a dispatch is a call that may have run and waits on someone to say whether it runs again; it is held, and its Dispatches say it may have run.
func (*Call) IdempotencyKey ¶ added in v0.0.11
IdempotencyKey returns the key the call's last dispatch handed its tool, or "" when it has no dispatch or the dispatch no key. It is the key a further run of that hand-off repeats, and it pairs with Call.DispatchedArgs: a harness that ran the call again under a new key, which it does for new arguments, made a new operation, and the key of an earlier dispatch describes the earlier arguments.
func (*Call) InFlight ¶ added in v0.0.11
InFlight reports whether the call was handed to its tool when the record stopped: it has a dispatch and no output, and no decision after its last dispatch holds it or answers it. Its side effect may have happened.
func (*Call) Rejected ¶ added in v0.0.5
Rejected reports whether a decision ended the call without running it.
func (*Call) State ¶ added in v0.0.5
State returns what the path says happened to the call, reading the header's records to decide whether a missing dispatch means the call never started or means the file does not say. A held call with dispatches is held, and may have run. The promise covers a call the session made, after its base: one in a fork's prefix, from a call Session.Calls or Session.PendingCalls returns, is unknown, since its origin may have promised nothing. So is one with no dispatch on the path and one on another branch of the session, which a rebase above the dispatch leaves. A call from Calls over a bare path is taken to be the session's own, with nothing beside the path. A prefix call's dispatches, when its origin holds any, are what OriginDispatches reads through a store; they do not change the state, which is unknown either way, but say under which key and with which arguments the call may have run.
type CallState ¶ added in v0.0.5
type CallState int
CallState is what the path says happened to a call.
const ( // CallCompleted: the call has an output. CallCompleted CallState = iota // CallHeld: the call waits on an answer to a hold. CallHeld // CallInFlight: the call was dispatched and no output arrived, so // its side effect may have happened. CallInFlight // CallNeverStarted: no dispatch and no output, for a call the // session made under a header that promises dispatches are recorded // before the tool runs, so the tool never ran. // A call in a fork's prefix was made under its origin's promise, // which this header does not vouch for, and is CallUnknown. CallNeverStarted // CallUnknown: no dispatch and no output, where no promise covers // the call, so the file does not say whether the tool ran. CallUnknown // CallAnswered: an answer decision ended the call and its output is // not on the path yet, since the record stopped between the two. // The harness that continues the path writes the output and // nothing else for the call; no dispatch may follow. CallAnswered // CallRejected: a reject decision ended the call and its refusal // output is not on the path yet, since the record stopped between // the two. The call did not run and does not: the harness that // continues the path writes the output, carrying the reject's // reason, and nothing else for the call. CallRejected )
type Callee ¶ added in v0.0.24
Callee names a callee in another system, reached over a protocol such as A2A, for a RelSubsession or RelPeer link: the endpoint the call was sent to as the caller resolved it (never a name the callee claims for itself), the callee's task (empty when it answered without one, as an A2A message does) and conversation IDs, and its own session ID when it publishes one.
type CalleeLink ¶ added in v0.0.24
type CalleeLink struct {
*LinkEntry
// Dispatch is the entry ID of the dispatch the link belongs to: for
// a callee in another system (URL set), the latest dispatch of its
// call before it on the path, since the callee names its work only
// when it answers; for a subsession of this store, written before
// the hand-off it starts, the first dispatch of its call after it.
// It is empty when the path records no such dispatch.
Dispatch string
// Cancelled is true when a cancel notice on the path names this
// link: the caller asked the callee to stop. It does not say the
// work stopped.
Cancelled bool
// Answered is true when the call's output on the path came from
// this callee: the output follows the link, the link belongs to the
// call's last dispatch before the output, no cancel notice names
// it, and no answer decision of the call comes between the link
// and the output, which would make the output the harness's.
Answered bool
}
CalleeLink is one callee of a call as a path records it: a subsession or peer link, the hand-off it belongs to, and what became of it.
func Callees ¶ added in v0.0.24
func Callees(path []Entry) []CalleeLink
Callees returns the callees of the calls on a root-first path, every subsession and peer link in path order. A link is identified by its entry and the dispatch it belongs to, not by its task, which a callee that answered with a message has none of: a call dispatched more than once, to a callee that starts new work each time, has a link for each hand-off that reached one. A cancel notice is not a callee; it marks the link its target names, and one naming no link on the path is passed over.
type Change ¶ added in v0.0.21
type Change struct {
Kind ChangeKind
// Session is the follower's own session after this change, which
// the next step extends in place: Path, ContextAt and Verify work on
// it as on any read session. It is never the writer's, and a caller
// never appends to it.
Session *Session
// ID and Entry are the entry an Appended change adds.
ID string
Entry Entry
// Leaf is the leaf a Head change records.
Leaf string
// Cursor is the position after this change, to resume from.
Cursor Cursor
}
Change is one step of a follow.
type ChangeKind ¶ added in v0.0.21
type ChangeKind int
ChangeKind says what a Change is.
const ( // Snapshot: Session is the session as it was read. Snapshot ChangeKind = iota // Appended: Entry was appended and Session includes it. Appended // Head: the store recorded Leaf as the session's head. A leaf a // writer moved with Session.Branch and did not record is not seen. Head // Reset: the log was replaced. Session is the session read again, // and a consumer drops what it derived from before. It may lack an // entry that was yielded earlier. Reset )
func (ChangeKind) String ¶ added in v0.0.21
func (k ChangeKind) String() string
String names the kind.
type CompactionEntry ¶
type CompactionEntry struct {
EntryBase `json:"-"`
// FirstKept names the earliest entry on the path that stays in
// context.
FirstKept string `json:"first_kept"`
// Summary is an item: the server's compaction item verbatim, or a
// message for a local summary.
Summary openresponses.Item `json:"summary"`
// Pinned are items kept verbatim from before FirstKept. They are in
// context immediately after Summary and before the entries from
// FirstKept.
Pinned openresponses.Items `json:"pinned,omitempty"`
// Config is a full settings checkpoint so a reader need not replay
// config entries from before the compaction.
Config Settings `json:"config"`
TokensBefore int `json:"tokens_before,omitempty"`
Usage *openresponses.Usage `json:"usage,omitempty"`
}
CompactionEntry replaces the context before FirstKept with a summary. It is in context through its summary and its pinned items.
func (*CompactionEntry) EntryType ¶
func (*CompactionEntry) EntryType() string
EntryType returns "compaction".
func (*CompactionEntry) MarshalJSON ¶
func (e *CompactionEntry) MarshalJSON() ([]byte, error)
MarshalJSON emits the entry as one JSON object.
func (*CompactionEntry) UnmarshalJSON ¶
func (e *CompactionEntry) UnmarshalJSON(data []byte) error
UnmarshalJSON decodes the entry.
type ConfigEntry ¶
type ConfigEntry struct {
EntryBase `json:"-"`
Model string `json:"model,omitempty"`
Instructions *string `json:"instructions,omitempty"`
Reasoning *openresponses.ReasoningConfig `json:"reasoning,omitempty"`
Text *openresponses.TextConfig `json:"text,omitempty"`
// InstructionsParts names the parts the instructions are composed
// of, in the order they are joined. A delta carries the whole
// ordered list: a part whose text changed carries its text, a part
// whose text is unchanged carries its Hash alone, and a part left
// out of the list is removed. Set it through
// [Settings.InstructionsDelta] rather than by hand, which computes
// exactly that from the parts in force. When Instructions is set
// beside it the two must agree, since Instructions is the parts
// joined with "\n\n".
InstructionsParts []InstructionPart `json:"instructions_parts,omitempty"`
// InstructionsOmitted records the parts the writer considered and
// left out, so a session says what the model was not given as well
// as what it was. Nothing in it reaches the request, but it stays
// in force until a later config entry carries it: nil leaves the
// list in force as it was, and an empty, non-nil list, written as
// [], clears it. A writer sets it only when the list changed, and
// through [Settings.OmittedDelta], which names each run of parts
// that stay omitted by a keep, and, in a session of format 0.11, a
// run of the list an earlier entry wrote by a keep carrying of.
InstructionsOmitted []OmittedPart `json:"instructions_omitted,omitzero"`
ToolsAdded openresponses.Tools `json:"tools_added,omitempty"`
ToolsRemoved []string `json:"tools_removed,omitempty"`
// Extra carries request members beyond the named ones, such as
// temperature or provider passthrough keys. A null value removes
// the key from the settings.
Extra map[string]json.RawMessage `json:"extra,omitempty"`
Replace bool `json:"replace,omitempty"`
// Omit records the items a request leaves out of the context, in
// format 0.11: a rule that leaves out a reasoning item written under
// another model, and a list of entries whose items contribute
// nothing. It is in force as a setting is: nil leaves the object in
// force as it was, an empty, non-nil one, written as {}, clears it,
// and any other sets the reasoning rule it names and adds its items
// to those in force. See [Omit] and [Settings.OmitDelta]. An omit
// member that does not decode as the object, which a file from before
// the member was defined may hold, is kept as written in Unknown and
// Omit is nil.
Omit *Omit `json:"-" member:"omit"`
}
ConfigEntry is a delta to request settings. Fields left at their zero value are unchanged; Replace discards all earlier config on the path before this one applies. The first entry on a root should be a config carrying full settings.
func ConfigFromRequest ¶
func ConfigFromRequest(req openresponses.Request) (*ConfigEntry, error)
ConfigFromRequest builds the full config entry for the settings a request was sent with: its model, instructions, reasoning, text and tools, with every other request member that is not about the one call (temperature, tool_choice, metadata, provider passthrough keys and so on) in Extra under its wire name. Replay of the result over empty settings followed by Settings.Request over the same input hashes to the same value as the request, which is what the first first entry should establish. Replace is set so the entry stands alone wherever it is placed.
func ConfigFromRequestParts ¶ added in v0.0.6
func ConfigFromRequestParts(req openresponses.Request, parts ...InstructionPart) (*ConfigEntry, error)
ConfigFromRequestParts is ConfigFromRequest for a caller that composed the instructions from named parts: the entry carries the parts instead of the joined string, so every delta after it names the one part that moved rather than repeating the whole prompt. The parts' texts joined with PartSeparator must equal the request's instructions, since that is what the model received and what the request hash covers.
func UnresolvedOf ¶ added in v0.0.20
func UnresolvedOf(path []Entry) []*ConfigEntry
UnresolvedOf returns the config entries on a root-first path that carry a keep with an of the path cannot resolve, in path order: the of names no entry on the path whose list is still in force to name, since a replace or a compaction's checkpoint, or the keep runs past that list or takes a part the delta names elsewhere. The format keeps such an element as written and it reaches no request, so nothing but a tool that checks a file reports it; this is that check, replaying the path as the context algorithm does.
func (*ConfigEntry) ClearExtra ¶ added in v0.0.3
func (c *ConfigEntry) ClearExtra(key string)
ClearExtra records the removal of a passthrough member: the delta carries a null for key, which deletes it from the settings on replay.
func (*ConfigEntry) MarshalJSON ¶
func (e *ConfigEntry) MarshalJSON() ([]byte, error)
MarshalJSON emits the entry as one JSON object.
func (*ConfigEntry) SetExtra ¶ added in v0.0.3
func (c *ConfigEntry) SetExtra(key string, v any) error
SetExtra records a passthrough request member on the delta: v is marshalled and stored under key, replacing any earlier value.
func (*ConfigEntry) UnmarshalJSON ¶
func (e *ConfigEntry) UnmarshalJSON(data []byte) error
UnmarshalJSON decodes the entry.
type Context ¶
type Context struct {
Settings Settings
Items openresponses.Items
// Entries are the entries selected by the context algorithm, in
// order: after a compaction, the compaction itself, then the kept
// entries from FirstKept up to it, then everything after. Entries
// that contribute no item are included so a renderer can show them.
Entries []Entry
// ItemEntries is aligned with Items: ItemEntries[i] is the entry
// that contributed Items[i], so an index into the request input
// maps back to the entry that produced it. After a compaction the
// compaction entry fills one slot for its summary and one for each
// of its pinned items, so it repeats as many times as it
// contributed: Items[0] is its summary and the next len(Pinned)
// items are its pins.
ItemEntries []Entry
// OmittedItems are the item entries on the path that the request
// leaves out, each with the reason, in path order: the entries the
// context algorithm selected whose items [Settings.Omit] excludes.
// They are in Entries, so a renderer can show them as omitted and
// not as absent, and in neither Items nor ItemEntries, so Request
// is what the model was sent. It is empty for a path with no omit
// in force.
OmittedItems []OmittedItem
// ReplacedItems are the item entries whose items the request sends
// with a placeholder in place of part of them, as [Omit.Outputs] and
// [Omit.Images] say, in path order, an entry both reach once for
// each. Items holds the item as sent, and the entry the item as
// recorded.
ReplacedItems []ReplacedItem
}
Context is what a model call at a point on a path receives: the replayed settings, the item list ready to be the request input and the entries the list was built from, for renderers.
func BuildContext ¶
BuildContext runs the context algorithm over a root-first path. Only the last compaction on the path is applied. FirstKept must name an entry on the path before the compaction. An item entry that the omit in force at the end of the path excludes contributes nothing and is reported in Context.OmittedItems.
func PathRequestContext ¶ added in v0.0.31
PathRequestContext is Session.RequestContext for a reader holding a root-first path and not its session, a projection among them: the context of the request that produced the response entry the path ends at, the path before it with the response's own output items removed.
func (Context) InstructionsOmitted ¶ added in v0.0.6
func (c Context) InstructionsOmitted() []OmittedPart
InstructionsOmitted returns the parts left out of the instructions in force: Settings.InstructionsOmitted. The list stays in force until a later config entry carries the member, so a writer writes it only when it changed, and a compaction checkpoint carries it.
func (Context) Request ¶
func (c Context) Request() (openresponses.Request, error)
Request returns the canonical request for the context. It equals the request the model was sent under canonical JSON (RFC 8785), not in bytes: member order inside opaque JSON, such as a tool's parameter schema, is what the store gives back. A jsonl file gives members as they were written and a cas store gives them in canonical order, so the same session rebuilds different bytes from each, with one request hash. A renderer that turns the request into tokens passes it, and the live request it compares with, through CanonicalRequest first.
func (Context) RequestJSON ¶ added in v0.0.31
RequestJSON returns the canonical request for the context as the JSON its request hash is taken over, every recorded setting exactly as recorded; see Settings.RequestJSON.
type Cursor ¶ added in v0.0.21
type Cursor string
Cursor is a position in one session's log, opaque and the store's own. The zero Cursor is the start: Follow begins with a snapshot. A cursor from one store is meaningless to another, and a cursor the current log no longer holds, because the log was replaced since, is answered with a Reset, never with a silent skip.
type CustomEntry ¶
type CustomEntry struct {
EntryBase `json:"-"`
NS string `json:"ns"`
Data json.RawMessage `json:"data,omitempty"`
// CallID names the function call the record belongs to, when the
// writer knows it: a record a tool writes while it runs. A record's
// position cannot say which call of a batch in flight it belongs to;
// this can. A reader may use it and must not require it. A call_id
// member that is not a non-empty string, which a file from before
// the member was defined may hold, is kept as written in Unknown and
// CallID is empty.
CallID string `json:"-" member:"call_id"`
}
CustomEntry is application state that is not in context. State that the model should see is an ItemEntry holding a namespaced item.
func (*CustomEntry) Calls ¶ added in v0.0.31
func (e *CustomEntry) Calls() int
Calls returns the number of model calls the entry records, which RFC 0001 has a custom entry record when its data is an object with a top-level usage member in the payload profile's usage shape: calls the path paid for that produced no response. They are the data's attempts member beside the usage, a positive integer with a response's meaning, or one when it is absent or in another form. An entry whose data holds no such usage records none, and Calls returns zero.
func (*CustomEntry) MarshalJSON ¶
func (e *CustomEntry) MarshalJSON() ([]byte, error)
MarshalJSON emits the entry as one JSON object.
func (*CustomEntry) UnmarshalJSON ¶
func (e *CustomEntry) UnmarshalJSON(data []byte) error
UnmarshalJSON decodes the entry.
type DecisionEntry ¶ added in v0.0.5
type DecisionEntry struct {
EntryBase `json:"-"`
CallID string `json:"call_id"`
Target string `json:"target"`
Verdict string `json:"verdict"`
By string `json:"by,omitempty"`
Reason string `json:"reason,omitempty"`
Args json.RawMessage `json:"args,omitempty"`
// Withdrawn marks, in format 0.14, a reject that withdraws the
// dispatch it directly follows: the writer wrote the dispatch and
// then did not hand the call to its tool. A reader reads that
// hand-off as never made and this decision as none, so the call is
// what it was before the dispatch, never started when it has no
// other; see [Call.Withdrawn] and [NewWithdrawal]. It is defined on
// a reject alone: a withdrawn member on another verdict, or one that
// is not true, is kept as written in Unknown and Withdrawn is false.
// A reject carrying it that follows no dispatch of its call directly
// reads as a reject without it.
Withdrawn bool `json:"-" member:"withdrawn"`
}
DecisionEntry records that a call's fate was decided outside the tool. Target is the item entry holding the function call. Reason is required when Verdict is VerdictReject, since it is what the model saw as the output. Args, when present, are the arguments the tool ran with after the decision rewrote them; the function call item stays as the model produced it. An answer, VerdictAnswer, ran nothing, so Args on one names nothing.
func NewDecision ¶ added in v0.0.5
func NewDecision(callID, target, verdict, by string) *DecisionEntry
NewDecision builds a decision on the call callID held by the item entry target. by may be "".
func NewWithdrawal ¶ added in v0.0.31
func NewWithdrawal(callID, target, by, reason string) *DecisionEntry
NewWithdrawal builds the reject that withdraws the dispatch of the call callID held by the item entry target: the writer wrote the dispatch and then did not hand the call to its tool. It is appended directly after that dispatch, before any other decision, dispatch or output of the call; reason says why the hand-off was not made.
func (*DecisionEntry) EntryType ¶ added in v0.0.5
func (*DecisionEntry) EntryType() string
EntryType returns "decision".
func (*DecisionEntry) MarshalJSON ¶ added in v0.0.5
func (e *DecisionEntry) MarshalJSON() ([]byte, error)
MarshalJSON emits the entry as one JSON object.
func (*DecisionEntry) UnmarshalJSON ¶ added in v0.0.5
func (e *DecisionEntry) UnmarshalJSON(data []byte) error
UnmarshalJSON decodes the entry.
func (*DecisionEntry) WithArgs ¶ added in v0.0.5
func (e *DecisionEntry) WithArgs(args json.RawMessage) *DecisionEntry
WithArgs records the arguments the tool ran with and returns the entry, for chaining. args must be a JSON value.
func (*DecisionEntry) WithReason ¶ added in v0.0.5
func (e *DecisionEntry) WithReason(reason string) *DecisionEntry
WithReason sets the decider's text and returns the entry, for chaining.
type DispatchEntry ¶ added in v0.0.5
type DispatchEntry struct {
EntryBase `json:"-"`
CallID string `json:"call_id"`
Target string `json:"target"`
// IdempotencyKey is the key the harness gave the tool for this
// hand-off. A dispatch that repeats an earlier hand-off carries that
// hand-off's key, and a new key is a new operation; the last
// dispatch's key is what [Call.IdempotencyKey] returns. An
// idempotency_key member
// that is not a non-empty string, which a file from before the
// member was defined may hold, is kept as written in Unknown and
// IdempotencyKey is empty.
IdempotencyKey string `json:"-" member:"idempotency_key"`
}
DispatchEntry records that a call was handed to its tool. Target is the item entry holding the function call. A writer that names "dispatch" in the header's records writes it, durably, before the tool runs. Each dispatch for a call is one hand-off, so a call run again after a restart has a second.
func NewDispatch ¶ added in v0.0.5
func NewDispatch(callID, target string) *DispatchEntry
NewDispatch builds a dispatch for the call callID held by the item entry target.
func (*DispatchEntry) EntryType ¶ added in v0.0.5
func (*DispatchEntry) EntryType() string
EntryType returns "dispatch".
func (*DispatchEntry) MarshalJSON ¶ added in v0.0.5
func (e *DispatchEntry) MarshalJSON() ([]byte, error)
MarshalJSON emits the entry as one JSON object.
func (*DispatchEntry) UnmarshalJSON ¶ added in v0.0.5
func (e *DispatchEntry) UnmarshalJSON(data []byte) error
UnmarshalJSON decodes the entry.
func (*DispatchEntry) WithIdempotencyKey ¶ added in v0.0.11
func (e *DispatchEntry) WithIdempotencyKey(key string) *DispatchEntry
WithIdempotencyKey records the key the tool is handed and returns the entry, for chaining.
type Entry ¶
Entry is one line of a session after the header. Concrete types are ItemEntry, ResponseEntry, ConfigEntry, CompactionEntry, BranchSummaryEntry, RunEntry, DispatchEntry, DecisionEntry, QueuedEntry, LabelEntry, InfoEntry, EnvEntry, OutcomeEntry, LinkEntry, CustomEntry and UnknownEntry. Decoded values are always pointers, so switch on *ItemEntry and so on. An entry must not be modified once it has been appended to a session; corrections are new entries.
func FileOrder ¶ added in v0.0.27
FileOrder returns a session's entries in file order, as RFC 0002 projects a session to a file and as a redacted entry's positions count them: the prefix, root first, then the session's own entries in the order the session holds them, which for a store's session is its log order. A projection's synthetic leaf marker is not an entry and is not there.
func RewriteStrings ¶ added in v0.0.27
RewriteStrings returns a new entry with f applied to every string in e's body; e is not changed. That is every string the entry's JSON holds, at any depth: its typed members, those the format does not define (Unknown), the members a reader keeps as written beside a typed one (a workspace's host), an extension entry's own members, the items of an item, a compaction and a branch summary, inline media as its data URL, whose base64 payload f is given undecoded, and the record of what a writer normalised. f is given member names too where a name is data rather than the format's (RFC 0001, Every redacted entry): the paths an env entry's files are keyed by and the tool names its tools are keyed by, and every name, at any depth, inside an object whose members the format does not define: a custom entry's data, an outcome's details, a decision's args, a config's extra, the value of any member the format does not define, and an extension entry's own members, their names included. Two names of one object that the rewrite makes one, or an extension entry's name made one the envelope holds, are refused with ErrKeyCollision.
f is never given any other member name, a number or a boolean: in an object the format defines, an entry, a trigger or a workspace among them, a member name is the format's, and stays as written even when the member is one the format does not define, whose value is data; and the names inside an item, which the payload profile defines, stay as written. Nor is f given the envelope (type, id, parent, parents, ts), legacy_id or redacted, which is kept as it was in whatever form it is held; a member naming an entry or a session, which RemapRefs re-points; a member holding a hash (request_hash, an instruction part's hash, an env entry's file hashes); or a sidecar media URL, sidecar:sha256: and the blob's hash, whose blob a redactor replaces under the replacement's hash, as the format has it. A call_id where the format defines one, on an item, a dispatch, a decision, a link or a custom entry, and a run end's pending call IDs, are left too: a call_id pairs a call with its output, its dispatches, decisions and links, and a subsession's ID is derived from it, so a redactor that must change call IDs changes every one in step itself, and the subsessions' IDs with them. A call_id inside a decision's args, a custom entry's data or any member the format does not define is the writer's text and is given to f. Each member is taken where the format defines it: one of those names in another form or place, such as an of beside an id, is body text.
An instruction part whose text changed has its hash, where it carries one, recomputed, and a part named by its hash alone, whose text is in an earlier entry, is given the hash of the rewritten text by a Rewriter that met it; one it did not meet keeps the original's hash and is reported by Rewriter.Unmet. Instructions that are the join of their parts, as the path resolves them, are not given to f whole: they are rebuilt as the join of the rewritten parts, so a rewrite cannot cross a part boundary. Instructions that are not such a join are given to f whole. Instructions beside a part the rewrite cannot resolve, a part named by hash or keep with its text in an entry it was not given, which RewriteStrings alone never is, are left when f leaves them and refused with ErrInstructionsJoin when it would change them.
In normalised, each was is JSON source text, and f is applied to each string value in it, which is written again in canonical form where f changed it, a lone surrogate escaped as it was; a was that is a number, the digits of a member the payload carries as a string, is given to f as those digits and written as the JSON string of what f returns when it changes them, since the record can then no longer be the original text and must not keep the value. Each raw is the base64 of the original bytes of a string, which f is given decoded, its escapes resolved and an ill-formed sequence as the bytes it was, and which is encoded again with the ill-formed bytes kept. Without that the original value would survive in the record, and in raw no string rewrite would ever see it. A record is rewritten as the member it describes: its at follows a path the rewrite renamed, and a record of a member f is not given is left.
f must be deterministic: one string gives one result wherever it appears, since a response_id and a part's text are named across entries by their value, and a rewrite that differs between two entries breaks the names. It is called in the same order on every run: an entry's members in sorted order, the elements of an array in index order, at every depth. A result that is not valid UTF-8 is repaired by the encoder, and a value the entry's type cannot hold, such as a closed member given a value it does not define, is an error from the decode.
The result keeps e's ID, parent and timestamp, and what it keeps as written is what the rewritten JSON holds: it never writes e's line. Its hashes are a session's to compute, by Session.Commit once the writer has cleared the ID, and its content hash is empty until then. A response's request_hash is the session's to compute too, over the request the session it is committed to rebuilds for it, which no rewrite of one entry can see: the result still holds the original's, or none, and a redactor sets it with Session.SetRequestHash just before the commit. A redacted session hashes independently, so the original's is never kept (RFC 0001, Redaction).
func UnmarshalEntry ¶
UnmarshalEntry decodes one line, dispatching on its type. Types this package does not define decode to *UnknownEntry.
type EntryBase ¶
type EntryBase struct {
// ID is unique within the session. Session.Append assigns one when
// it is empty.
ID string
// Parent is the ID of the parent entry, or "" for a root. It is the
// entry's line of descent: exactly one, in this session, and the
// only edge a context is built from.
Parent string
// Parents records further predecessors this entry converges: the
// result of a subagent session, a branch merged back, several
// workers joined at once. It is provenance. Nothing it names
// contributes to any context by virtue of being named — whatever
// crossed the boundary is in this entry's own payload, materialised
// — so [Session.Path] and every context the library builds follow
// Parent alone. Session.Append sorts it.
Parents []EntryRef
// Timestamp is when the entry was written. The format admits one
// spelling, UTC with at most nine fractional digits, so Append
// converts a caller's time to UTC.
Timestamp time.Time
// LegacyID is the id an entry had before its file was migrated to
// 0.5, when its id became the envelope hash. It is a body member,
// so it is hashed; a projection may emit it beside the new id so
// output made from the earlier file still resolves.
LegacyID string
// Normalised records what a writer changed in the body before
// writing so that it passed the I-JSON test: a lone surrogate to
// U+FFFD, an integer outside binary64 to a string or to its rounded
// value. It is sorted by At as UTF-16 code units.
Normalised []Normalisation
// Unknown holds body members this package does not define,
// preserved so a later minor version's optional fields survive a
// rewrite. It is nil when there are none.
Unknown map[string]json.RawMessage
// contains filtered or unexported fields
}
EntryBase is the envelope every entry carries.
func (*EntryBase) ContentHash ¶ added in v0.0.8
ContentHash returns the hash of the entry's body, the members outside the envelope, as computed when the entry was appended or read. It is empty for an entry that has been neither.
type EntryRef ¶ added in v0.0.8
type EntryRef struct {
// Session names the session Entry is in. It is empty when the entry
// is in this session, which is the only case a reader can resolve
// without a store.
Session string `json:"session,omitempty"`
// Entry is the ID of the entry referred to.
Entry string `json:"entry"`
// contains filtered or unexported fields
}
EntryRef names one entry, here or in another session. It is what EntryBase.Parents holds.
func (EntryRef) MarshalJSON ¶ added in v0.0.31
MarshalJSON writes the reference with the members it was read with that the format does not define.
func (*EntryRef) UnmarshalJSON ¶ added in v0.0.18
UnmarshalJSON decodes a reference by its members' exact names, as the format names them: a key in another case is a member the format does not define. An entry member that is missing or not a string decodes as no entry, which the convergence rules refuse.
type EnvEntry ¶
type EnvEntry struct {
EntryBase `json:"-"`
CWD string `json:"cwd,omitempty"`
VCS *VCS `json:"vcs,omitempty"`
Files *FileHashes `json:"files,omitempty"`
Tools map[string]string `json:"tools,omitempty"`
// Workspace says which file system CWD is a path in. A local run
// may leave it nil.
Workspace *Workspace `json:"workspace,omitempty"`
}
EnvEntry is a snapshot of the environment for replay: where the tools ran and what they saw. It applies from its position on the path until the next one, and its CWD takes precedence over the header's.
What a harness records beyond these members, such as the identity of a working tree's contents, goes in a namespaced member: one of the entry, kept in Unknown (EntryBase), or one inside vcs, set with VCS.SetMember. Only workspace is compared for a substitution, so a change to any other member, those the format does not define included, is not one: see SameWorkspace.
func NewEnvEntry ¶
NewEnvEntry starts an env entry for a working directory. Use EnvEntry.SetGit, EnvEntry.AddRead and EnvEntry.AddWritten to fill it in.
func (*EnvEntry) AddWritten ¶
AddWritten records the hash of a file the session wrote.
func (*EnvEntry) MarshalJSON ¶
MarshalJSON emits the entry as one JSON object.
func (*EnvEntry) SetGit ¶
SetGit records the git revision of dir, read from its .git directory without running git, and whether the tree is known to be dirty. A directory that is not a git checkout leaves VCS unset and returns nil; other errors are returned. Members of an existing VCS the format does not define, set with VCS.SetMember or read from a file, are kept.
func (*EnvEntry) SetWorkspace ¶ added in v0.0.5
SetWorkspace records which file system CWD is a path in: kind is WorkspaceLocal, WorkspaceContainer or WorkspaceRemote, and ref is what the harness resolves to it (an image digest, an endpoint), empty for a local workspace. The machine goes in the result's Host and a restarted file system's identity in its Instance, which the format defines since 0.14; anything else that tells one file system from another goes in through Workspace.SetMember on the result, and not as an unknown member of the entry, which the substitution rule does not compare.
func (*EnvEntry) UnmarshalJSON ¶
UnmarshalJSON decodes the entry.
type FileHashes ¶
type FileHashes struct {
Read map[string]string `json:"read,omitempty"`
Written map[string]string `json:"written,omitempty"`
}
FileHashes maps paths to content hashes ("sha256:...") for files read and written.
type Follower ¶ added in v0.0.21
type Follower interface {
Follow(ctx context.Context, id string, from Cursor) iter.Seq2[Change, error]
}
Follower is implemented by a store that can follow a session: read it, then receive each change the store accepts, without holding it.
Follow takes no hold and writes nothing, recovery included, as Reader does, so a store opened read-only can follow, and so can one that is not while another process holds the session. The iterator yields a Snapshot first, or with a cursor the changes after it, and then each change as the store accepts it. It ends without an error when ctx is done, and with ErrNoSession when the session is deleted; a session created again under the same ID is another session, and the follower never continues into it. Breaking out of the range stops the follower and releases what it held.
Everything a follower yields is visible, never promised durable, as Reader says of a read: a crash, or an append that failed after another process saw it, can take back an entry a follower yielded, and the follower then yields Reset. Appends arrive in the order the store accepted them, on every branch, never in the order of a path.
type HeadStore ¶ added in v0.0.31
HeadStore is implemented by a store that moves a session's head by compare-and-swap, which RFC 0002 (Head) asks of every store and every store this module offers implements. SetHead moves the head of the session from expected to to, and returns once the move is durable, as RFC 0002's Durability asks of a durable head move. Either may be "", the universal root every session's first entries hang from: the head there is the empty context, and the next append with no parent continues from it. A head that is not expected is ErrHeadMoved; a target that is not the root, the base or one of the session's own entries is ErrNoEntry, as is the root in a session with a base, and one that is a leaf label ErrHeadTarget.
A store that keeps sessions as RFC 0001 files, or as their lines, moves the head by appending a leaf label for to under its lock, after the compare; the session the store shares with its callers sees the move as an append. A store with a head of its own (the cas store) records the head. Either way a writer that appended a first entry to a session whose head is not the root, which is a branch at the root, moves the head onto it here. A store set to hold only redacted entries offers it too: the label a head move writes carries no content to have been redacted.
type Header ¶
type Header struct {
Format string `json:"format"`
ID string `json:"id"`
CreatedAt time.Time `json:"created_at"`
Payload string `json:"payload"`
Harness *Harness `json:"harness,omitempty"`
// Records lists the record entry types this writer writes whenever
// their event occurs, so a reader may take their absence on a path
// as the event not having happened. Empty means no such promise,
// which is what a converter over a native log without them sets.
Records []string `json:"records,omitempty"`
CWD string `json:"cwd,omitempty"`
ParentSession string `json:"parent_session,omitempty"`
// SpawnedBy is, for a subsession, the call_id of the parent's
// function call that spawned it.
SpawnedBy string `json:"spawned_by,omitempty"`
// Base is the hash of the entry this session continues from, in
// the session ParentSession names, or empty for a session that
// starts fresh. A file with a base opens with the path to it, the
// prefix, before any entry the session appended itself, and every
// own entry hangs from the base. A base is never a leaf label.
Base string `json:"base,omitempty"`
Media string `json:"media,omitempty"`
// Redacted marks a redacted session: one that holds entries a
// redactor made from entries of another session, its original, by
// changing what was written after it was written. It is a session of
// its own, with an ID of its own, whose hashes and references the
// redactor computed over what it wrote, so the file walks and
// verifies against itself and not against the original. It grows as
// the redactor redacts what the original appends, and may be
// resumed: each redacted entry carries its redacted member
// ([RedactedOf]), and an entry appended natively carries none.
Redacted bool `json:"redacted,omitempty"`
// RedactedFrom, on a redacted session, names its original: see
// [RedactedFrom]. It is defined from format 0.13 and is refused
// without Redacted, with [ErrRedactedFrom]. A redacted_from member
// in a file that declares an earlier minor, which was free to hold
// one in any form, or one in a later file that does not decode as
// the object, is kept in Extra as written and RedactedFrom is nil.
// A header naming an earlier minor that holds one is never raised
// to 0.13 or later, by Read in memory or by a writer before an
// append, which is refused: see [Header.Raise]. Nor is a file one of
// whose entries holds redacted under such a header: see
// [ErrNoRaise].
RedactedFrom *RedactedFrom `json:"-"`
// Extra holds header fields this package does not define.
Extra map[string]json.RawMessage `json:"-"`
}
Header is the first line of a session file. It is not part of the tree. Unknown fields decode into Extra and are written back, as the format requires of any tool that rewrites a file.
func RewriteHeaderStrings ¶ added in v0.0.31
RewriteHeaderStrings returns a copy of h with f applied to every string of it that is text, under the contract RewriteStrings states: cwd, the harness's name and version, and every string and member name at any depth in the value of a member the format does not define (Extra), whose own name stays, as in an entry. What a redaction writes, type, format, id, created_at, payload, records, media, redacted and redacted_from, is left for the redactor to set; base and parent_session are references, which RemapHeaderRefs re-points; and spawned_by is a call_id, which a redactor that changes call IDs changes in step with the parent's, as it does every call_id RewriteStrings leaves (RFC 0001, The redacted session). One table in this package says which header member is which, so a header that gains a member cannot drift out of step with a redactor.
func Scan ¶ added in v0.0.18
Scan reads a session file and verifies it as Read does, decoding no entry: every line passes the I-JSON test, every entry's id is the hash of its line's canonical bytes, its ts has the one spelling the format admits, its parent is null or an entry earlier in the file, its parents keep the convergence rules, in a file whose header names a base it keeps the base rule (ErrBaseRule), and the legacy_id and normalised any entry may carry have their types. The header is read and checked first and returned as the file declares it. A line met a second time is yielded with Repeat set. A last line cut short, as a crash mid-append leaves, ends the sequence with a *TruncatedLine error; any other failure ends it with its error, and the header's base not being in the file is reported once the file ends. Lines are numbered, and empty ones skipped, as Read numbers and skips them.
Scan checks no more of an entry than that: what a core entry type requires of its own members is Decode's to check, as it is Read's. The sequence reads r as it goes, so it ranges once; a second range yields an error.
func (Header) HasRecord ¶ added in v0.0.5
HasRecord reports whether the header promises that entries of type typ are written whenever their event occurs, so their absence means the event did not happen.
func (Header) MarshalJSON ¶
MarshalJSON emits the header with its type discriminator first and Extra flattened into the object.
func (*Header) Raise ¶ added in v0.0.27
Raise sets the header's format to format, as a writer does to the header of a session whose header names an earlier minor before it appends to it, so a reader of that minor refuses the session rather than misread what was appended. It refuses with ErrNoRaise, and leaves the header as it was, to raise to 0.13 or later a header that names an earlier minor and holds redacted_from, typed or in Extra. Raise does not compare the two formats: the caller raises only to a later one.
Raise sees the header alone, and so not an entry holding redacted under it, which also forbids the raise. Session.RaiseRefused is the whole rule, header and entries. Session.Append and Session.Prepare apply it, and a store prepares every entry on its session before it raises the stored header on an append; a store that raises one outside an append, as an exchange or a fork's create does, calls RaiseRefused first.
func (*Header) UnmarshalJSON ¶
UnmarshalJSON decodes the header, keeping unknown fields in Extra.
func (Header) Validate ¶
Validate checks the required header fields, that the format is one this package reads, and that a redacted_from names an original other than the session and is on a redacted session, as ErrRedactedFrom says.
type Importer ¶ added in v0.0.27
type Importer interface {
Import(ctx context.Context, r io.Reader, asRecord bool) (*Session, error)
}
Importer is implemented by a store that takes a session in whole from an RFC 0001 file, as RFC 0002's projection section has a store import a projection, a redacted session among them. Import verifies every line and refuses a file one of whose lines fails, a last line cut short, a header carrying redacted_from without redacted (ErrRedactedFrom), a session ID the store holds (ErrSessionExists), an own entry that does not hang from the base or another own entry, and a fork whose media differs from that of a held session whose own entries include the base; a store set to hold only redacted entries refuses a file whose header does not carry redacted or that holds an entry without it, prefix included (ErrNotRedacted). What it imports is committed before it returns, and the session comes back as the store holds it.
asRecord says the file is the only copy, so the store is the session's record and not a mirror of it, as RFC 0002's record mark has it. A store that keeps no record mark holds every session as its own, and does not read it.
type InfoEntry ¶
InfoEntry carries display metadata such as a session name. Further members are kept in Unknown.
func (*InfoEntry) MarshalJSON ¶
MarshalJSON emits the entry as one JSON object.
func (*InfoEntry) UnmarshalJSON ¶
UnmarshalJSON decodes the entry.
type InstructionPart ¶ added in v0.0.6
type InstructionPart struct {
// ID is the part's stable name, chosen by the harness: the same
// string across the session, so a delta can name a part it does
// not repeat. It is empty on a keep, and on a part in force that
// an element naming nothing left unresolved.
ID string `json:"id,omitempty"`
// Text is the part's text. On a delta it is absent for a part
// whose text is unchanged, which carries Hash instead.
Text string `json:"text,omitempty"`
// Source names the layer that produced the part, in the harness's
// own terms.
Source string `json:"source,omitempty"`
// Hash is the hash of the text a delta does not repeat, in the
// format's notation: [HashPrefix] and the SHA-256 of the text.
// [HashText] computes it. It is set on a delta's unchanged part
// and empty on a part that carries its text.
Hash string `json:"hash,omitempty"`
// Keep, on a delta, stands for the next Keep parts in force,
// unchanged, and is the element's only member: see
// [Settings.InstructionsDelta]. A keep member that is not a
// positive integer, which a file from before the member was defined
// may hold, is kept as written and Keep is zero; on an element that
// names an ID it means nothing.
Keep int `json:"keep,omitempty"`
// contains filtered or unexported fields
}
InstructionPart is one named part of the instructions. A harness that composes the instructions from several layers, a product prompt, an AGENTS.md chain, a skill catalogue, a memory block, gives each layer a part, so a change to one is recorded as a change to one and a reader can say which layer an instruction came from.
func (*InstructionPart) UnmarshalJSON ¶ added in v0.0.10
func (p *InstructionPart) UnmarshalJSON(data []byte) error
UnmarshalJSON decodes the part, taking keep only when it is a positive integer written as digits, so an earlier file's keep in any other form stays a member this package does not define.
func (InstructionPart) Unresolved ¶ added in v0.0.10
func (p InstructionPart) Unresolved() bool
Unresolved reports whether the path could not rebuild the part's text: it was named by a hash or a keep the path could not resolve, or by an element that names nothing.
type ItemEntry ¶
type ItemEntry struct {
EntryBase `json:"-"`
// Item is the Open Responses item, verbatim. Extension items decode
// to *openresponses.UnknownItem and re-encode byte for byte.
Item openresponses.Item `json:"item"`
// ResponseID names the response this item belongs to when the item
// was model output; it matches ResponseEntry.ResponseID.
ResponseID string `json:"response,omitempty"`
// Visible is false for an item that is in context but that a
// renderer should hide. Nil means visible.
Visible *bool `json:"visible,omitempty"`
// Source says how the input arrived, for an item a person or
// another system sent rather than the model or the loop. It is the
// trigger a [QueuedEntry] carried, so two people steering one run
// are told apart.
Source *Trigger `json:"source,omitempty"`
// QueuedFrom names the [QueuedEntry] this item was accepted as,
// for an input that waited before it could be appended.
QueuedFrom string `json:"queued_from,omitempty"`
// Failed marks, in format 0.14, an entry holding a function call
// output whose tool reported failure, or which the harness gave up:
// cut off, timed out, its outcome unknown. The output describes it.
// It is a record member: the item is sent as it is, and the call is
// completed by its output all the same. A reader maps it to its
// protocol's failure state; [Call.Failed] reads it. A failed member
// on any other item, or one that is not true, is kept as written in
// Unknown and Failed is false.
Failed bool `json:"-" member:"failed"`
// FromLink names, in format 0.14, a peer link entry before this item
// on its path, by ID: the item carries that callee's later report,
// such as the result a peer whose work outlived its call delivers
// some turns after the call's acknowledgement. It is provenance: it
// does not make the call answered, and it reaches no request.
// [Session.VerifyRecords] reports one that names no peer link before
// the item. A from_link member that is not a non-empty string is
// kept as written in Unknown and FromLink is empty.
FromLink string `json:"-" member:"from_link"`
}
ItemEntry is one conversation item in the payload profile. It is in model context.
func NewItemEntry ¶
func NewItemEntry(item openresponses.Item) *ItemEntry
NewItemEntry builds an item entry.
func OutputEntries ¶ added in v0.0.6
func OutputEntries(path []Entry, resp *ResponseEntry) []*ItemEntry
OutputEntries returns the item entries on path that hold the output of resp, in path order. It is the format's rule for finding a response's own output items, stated in RFC 0001 under "Request context of a response", and it is the one implementation: a reader that needs those items, and one that needs to exclude them, must agree or the same file rebuilds two different requests.
Walking back from the end of path: an entry that is not an item entry is skipped, an item entry whose Response names resp is one of its output items, and the walk stops at the first item entry that names another response or none. A response with no ResponseID has no output items.
path is resp's path with resp itself at the end, or that path without it; either gives the same answer, since an entry that is not an item entry is skipped. It MUST NOT run past resp: a path that continues beyond the response ends in the next call's items, the walk stops on the first of them, and the result is an empty slice rather than an error. Only the matching item entries are returned, never the entries skipped between them: those are on the path for their own reasons and stay there.
The returned entries are the session's own, not copies. A caller that serves their items to something that records must clone them; a caller that only reads the path need not.
func (*ItemEntry) MarshalJSON ¶
MarshalJSON emits the entry as one JSON object.
func (*ItemEntry) UnmarshalJSON ¶
UnmarshalJSON decodes the entry, dispatching the item through the openresponses item registry.
type LabelEntry ¶
type LabelEntry struct {
EntryBase `json:"-"`
Target string `json:"target"`
Label *string `json:"label"`
}
LabelEntry bookmarks another entry. A nil Label clears an earlier label on the same target. An empty Target, null on the wire, names the universal root, which only a leaf label marks: the head at the empty context.
func NewLabelEntry ¶
func NewLabelEntry(target, label string) *LabelEntry
NewLabelEntry builds a label on target; an empty label clears.
func (*LabelEntry) MarshalJSON ¶
func (e *LabelEntry) MarshalJSON() ([]byte, error)
MarshalJSON emits the entry as one JSON object. An empty Target is written null: a leaf label on the universal root.
func (*LabelEntry) UnmarshalJSON ¶
func (e *LabelEntry) UnmarshalJSON(data []byte) error
UnmarshalJSON decodes the entry.
type LinkEntry ¶
type LinkEntry struct {
EntryBase `json:"-"`
Rel string `json:"rel"`
Session string `json:"session"`
// CallID ties a subsession to the function call that spawned it.
CallID string `json:"call_id,omitempty"`
// Target, on a [RelJudgedBy] link, names the entry of the judged
// session the judgement is about: the entry the judge's outcome
// names. On a cancel notice (Cancel set) it names the link, in this
// session, of the callee the cancel was sent to. It is a member of
// those links alone: a target member on another link, or one that
// is not a non-empty string, which a file from before the member
// was defined may hold, is kept as written in Unknown and Target is
// empty.
Target string `json:"-" member:"target"`
// URL, Task and Context, on a [RelSubsession] or [RelPeer] link,
// name a callee in another system, reached over a protocol such as
// A2A: the endpoint the call was sent to as the caller resolved it,
// never a name the callee claims; the callee's ID for the work the
// call started, absent when the callee answered without one (an A2A
// message rather than a task); and its ID for the conversation that
// work belongs to. Task and Context come with a URL. Session is then
// the callee's own session ID when it publishes one, and otherwise
// empty. Like Target, each is a member of those relations alone,
// kept as written in Unknown on any other, when it is not a
// non-empty string, or (task, context) when no url is beside it.
URL string `json:"-" member:"url"`
Task string `json:"-" member:"task"`
Context string `json:"-" member:"context"`
// Cancel, on a later [RelSubsession] or [RelPeer] link of the same
// call, is [CancelSent]: the caller told the callee the link Target
// names that it no longer wanted the work. It records the request,
// not that the work stopped. A link carrying it names no new
// callee; see [NewCancelSentLink]. Any other value, or one with no
// target beside it, is kept as written in Unknown.
Cancel string `json:"-" member:"cancel"`
}
LinkEntry references another session, for subagents and forks.
func Judges ¶ added in v0.0.20
Judges returns the judged_by links on a root-first path, in path order: the sessions that judged what the path holds, each with the entry it judged in its Target when the writer named one. A link is a record entry and contributes nothing to a context, so a reader selecting the judges of a run reads them here and filters by Target for the judgements of one entry; the header of a judge's session cannot say it is one, since a subagent and a fork carry the same parent_session.
func NewCalleeSubsessionLink ¶ added in v0.0.24
NewCalleeSubsessionLink builds the subsession link for a callee in another system that the call callID started, whose work the caller ends, or asks to stop, when it gives the call up: the relation follows the callee's lifetime, not where it runs. It is written as soon as the caller learns the callee's task, after the call's dispatch; a call dispatched again starts another task and gets a link of its own. Session.VerifyLinks does not check its session's header, which was not derived from this one.
func NewCancelSentLink ¶ added in v0.0.24
NewCancelSentLink builds the notice a caller records when it tells the callee link names that it no longer wants the work: a link of the same relation and call, carrying CancelSent, whose Target is link's entry ID, so link must have been appended, on the path the notice is appended to. It records that the caller asked, not that the work stopped; the caller does not wait for the callee's answer. It names no new callee, and Callees reads it as a notice about link. Its target names an entry of this session: whoever copies or re-points links, onto a new branch or into another store, re-points the notice with them.
func NewJudgedByLink ¶ added in v0.0.20
NewJudgedByLink builds the link entry a judged session records for the session of its judge: judge is the judge's session ID, and target, when it is not empty, the entry of the judged session the judgement is about, the one the judge's outcome names. The judge is a session of its own whose header names the judged one as its parent_session, which a subagent's and a fork's header does too, so the link is what says which it is. Judges reads these links back.
func NewLinkEntry ¶
NewLinkEntry builds a link to another session.
func NewPeerLink ¶ added in v0.0.24
NewPeerLink builds the link a caller records for a callee whose work outlives the call callID: the caller does not end it when it gives the call up, and the callee keeps its own record. It is written as soon as the caller learns the callee's task, or its answer when it gives none. From it a reader may conclude that the call's output came from that work, and not that the work can be replayed or that it ended with the call. A peer link names its call and a url or a session, which Session.Append and Session.Prepare hold a new one to.
func NewSubsessionLink ¶
NewSubsessionLink builds the link entry the format prescribes for a subagent session spawned by the function call callID.
func (*LinkEntry) MarshalJSON ¶
MarshalJSON emits the entry as one JSON object.
func (*LinkEntry) UnmarshalJSON ¶
UnmarshalJSON decodes the entry.
type LinkError ¶ added in v0.0.20
type LinkError struct {
// Entry is the link entry's id.
Entry string
// Session is the session the link names.
Session string
// Err says what is wrong with the link.
Err error
}
LinkError is the error Session.VerifyLinks returns for a link it could not pass: the link entry, the session it names, and what went wrong, which is ErrLinkMismatch naming the member that disagrees, or the error the resolver returned for the target.
type ListFilter ¶
type ListFilter struct {
// CWD matches the header's working directory exactly.
CWD string
// ParentSession matches sessions forked or spawned from the given
// one. A header cannot say which: a judge, a subagent and a fork
// all carry the session they came from, so they are listed
// together. The link entries of the parent say which each is, as
// [Judges] reads for judges.
ParentSession string
// Harness matches the name of the header's harness exactly; a
// header naming no harness matches nothing when it is set. A
// scheduled routine finds its own sessions by it without an index
// of its own beside the store.
Harness string
// Extra matches the header's extra members: each member named here
// must be in the header with the same value, compared in canonical
// form so key order and spacing do not matter; members the header
// has beyond these do not count. A harness that writes its user or
// platform into the header selects by them here.
Extra map[string]json.RawMessage
// TopLevel keeps only sessions with no spawned_by: those a person or
// a schedule started, and not the subsessions a call spawned.
TopLevel bool
// After and Before bound created_at, exclusive.
After, Before time.Time
// Limit caps the number of results; zero means no cap.
Limit int
// WithNames asks for Summary.Name. A store that keeps names
// indexed fills it regardless; a file store must scan each
// session's entries to find it, so it does so only when asked.
WithNames bool
// Current excludes sessions a continued_in link has retired, so a
// listing shows the successor and not the session it replaced.
// A file store scans each session's entries to know, as for
// WithNames.
Current bool
}
ListFilter narrows a List. Zero fields do not filter, so the zero filter lists every session. The header fields cost no scan: every store reads the header to list a session, and Matches decides on it alone; WithNames and Current read the session's entries.
func (ListFilter) Keep ¶ added in v0.0.5
func (f ListFilter) Keep(sum Summary) bool
Keep reports whether a summary passes the filter: the header checks of Matches, and Current against Summary.SupersededBy.
func (ListFilter) Matches ¶
func (f ListFilter) Matches(h Header) bool
Matches reports whether a header passes the filter: the fields of the filter that read the header alone, which is every one but WithNames and Current.
type MediaImporter ¶ added in v0.0.31
type MediaImporter interface {
ImportMedia(ctx context.Context, r io.Reader, media fs.FS, asRecord bool) (*Session, []string, error)
}
MediaImporter is implemented by a store that imports a sidecar session with its blobs in one step, as RFC 0002's import of a projection takes the session's directory with the file. See ImportMedia.
type MemoryOption ¶ added in v0.0.27
type MemoryOption func(*MemoryStore)
MemoryOption configures a MemoryStore.
func MemoryRedactedOnly ¶ added in v0.0.27
func MemoryRedactedOnly() MemoryOption
MemoryRedactedOnly sets the store to hold only redacted entries, as RFC 0002 lets a store be set: it refuses, with ErrNotRedacted, a session whose header does not carry redacted, at Create or Import, and every entry that carries no redacted member, however it arrives: an Append, an Import, the prefix of a fork Create makes. A fork's own entries and a resumed redacted session's native entries are refused alike, since neither was redacted.
That holds for what comes through the store. The store hands out its own sessions, from Create, Open and Import, and an entry added through one of them, by Session.Append, Commit or Extend, never reaches the store's checks, so such a store holds whatever its callers write that way. A store that must fail closed against its callers is a cas store set with its WithRedactedOnly.
type MemoryStore ¶
type MemoryStore struct {
// contains filtered or unexported fields
}
MemoryStore keeps sessions in memory. It is the reference store for tests and for sessions that are never persisted.
func NewMemoryStore ¶
func NewMemoryStore(opts ...MemoryOption) *MemoryStore
NewMemoryStore returns an empty store.
func (*MemoryStore) Blob ¶ added in v0.0.31
Blob implements BlobReader. The bytes returned are the caller's.
func (*MemoryStore) Create ¶
Create implements Store. A header whose Base is set makes a fork of the session holding the base, as Fork does; a redacted session is created as any other is. The session returned is the store's own, shared with it, so a write through it, by Session.Append, Commit or Extend, bypasses every rule the store holds an append to, MemoryRedactedOnly's among them.
func (*MemoryStore) Delete ¶
func (m *MemoryStore) Delete(_ context.Context, id string) error
Delete implements Store.
func (*MemoryStore) Follow ¶ added in v0.0.21
Follow implements Follower. A cursor is good for the session as it was created, so a session deleted and created again under its ID answers a cursor from before with a Reset. Appends through the store wake the follower directly; one made through the session returned by Open is found by a slow look, since nothing rings.
func (*MemoryStore) Import ¶ added in v0.0.27
Import implements Importer: the file is read with ReadForImport, and the session it holds is kept as read, its leaf the resume rule's, and returned as the store holds it, as Create returns one. A session this store holds is refused, as is a fork whose media differs from that of a held session whose own entries include the base, and, in a store set with MemoryRedactedOnly, a session whose header does not carry redacted or that holds an entry without it. The store keeps no record mark, so asRecord is not read. The session returned is the store's own, shared with it, so a write through it, by Session.Append, Commit or Extend, bypasses every rule the store holds an append to, MemoryRedactedOnly's among them.
func (*MemoryStore) List ¶
func (m *MemoryStore) List(_ context.Context, f ListFilter) iter.Seq2[Summary, error]
List implements Store.
func (*MemoryStore) Open ¶
Open implements Store. The session returned is the store's own, shared with it, so a write through it, by Session.Append, Commit or Extend, bypasses every rule the store holds an append to, MemoryRedactedOnly's among them.
func (*MemoryStore) PutBlob ¶ added in v0.0.31
PutBlob implements BlobWriter: it keeps a copy of the blob under its hash. Storing a blob the store holds again restarts its grace for MemoryStore.SweepBlobs.
func (*MemoryStore) Read ¶ added in v0.0.19
Read implements Reader: a copy of the session, rebuilt from its encoding. Its leaf is the one the entries record, as a store reading a file finds it: a leaf moved through Session.Branch and not recorded is not there.
func (*MemoryStore) ResolveRef ¶ added in v0.0.21
ResolveRef implements RefStore.
func (*MemoryStore) SetHead ¶ added in v0.0.31
func (m *MemoryStore) SetHead(ctx context.Context, sessionID, expected, to string) error
SetHead implements HeadStore: the compare and the leaf label that records the move are one step on the store's session, so no append comes between them, and MemoryStore.Read and a follower see the move as that label. A store set with MemoryRedactedOnly takes it too: the label carries no content to have been redacted.
func (*MemoryStore) SweepBlobs ¶ added in v0.0.31
SweepBlobs implements BlobSweeper: it removes each blob no session names that was stored longer than grace ago. It holds the store's writers off for its length, so no append names a blob between its reading the sessions and its removing one.
type Normalisation ¶ added in v0.0.8
type Normalisation struct {
// At is an RFC 6901 JSON Pointer relative to the body.
At string `json:"at"`
// Was is the member's original JSON source text, escapes included,
// so it is I-JSON whatever it describes. It is empty when the
// output was not valid UTF-8 and had no JSON text to record.
Was string `json:"was,omitempty"`
// Raw carries the original bytes, base64 under RFC 4648 §4 with
// padding, when Was cannot: output that was not valid UTF-8.
Raw string `json:"raw,omitempty"`
}
Normalisation is one change a writer made to a body before writing it, recorded in EntryBase.Normalised.
func NormaliseJSON ¶ added in v0.0.8
func NormaliseJSON(data []byte) ([]byte, []Normalisation, error)
NormaliseJSON rewrites a JSON document so that every value passes the I-JSON test the format hashes under, and reports each change the way the format has a writer record it. A whole number outside binary64 is rounded to binary64 and written canonically, so 9007199254740993 becomes 9007199254740992 however it was spelled and a 64-bit id such as 1234567890123456789 becomes 1234567890123456800; a lone surrogate escape becomes U+FFFD; in a string that is not valid UTF-8 each maximal subpart of an ill-formed sequence (Unicode §3.9) becomes one U+FFFD, as the WHATWG decoder does. Each change carries the RFC 6901 pointer of the value relative to data and the value's original JSON source text, a string's quotes included, or its original bytes in base64 when there was no valid text to record. The changes are sorted by pointer as UTF-16 code units, the order the format fixes so two writers normalising one output agree.
Session.Append runs this over an entry's body, and repairs a Go string that is not valid UTF-8 the same way, so a harness that hands the library a body straight from a provider need do nothing. It is exported for the case Go's decoder hides: a lone surrogate in provider bytes is replaced on the way into a struct, and the record that it happened is lost unless the harness normalises the bytes first and keeps the changes, prefixing each pointer with that of the member the bytes become (for an item, "/item") before setting them on EntryBase.Normalised.
What cannot be normalised is an error: a number that is not finite in binary64, a repeated member name, a member name that itself fails the test.
type Omit ¶ added in v0.0.20
type Omit struct {
// Reasoning is a rule over the reasoning items on the path. It is
// closed: [OmitOtherModels] is the one value defined, and a value
// this package does not define has no effect, leaving the rule in
// force as it was.
Reasoning string `json:"reasoning,omitempty"`
// Items are entry IDs. An item entry on the path whose ID is listed
// contributes nothing, whatever it holds; an ID that names no item
// entry on the path names nothing, and a writer writes no empty one.
// The set in force is the union of every list since the last replace
// or compaction's checkpoint.
Items []string `json:"items,omitempty"`
// Outputs, from 0.14, sends the function call output of each item
// entry it lists with a placeholder text in place of its output.
Outputs *OmitOutputs `json:"outputs,omitempty"`
// Images, from 0.14, sends input_image parts with a placeholder text
// in their place: all but the last few, or all before an entry.
Images *OmitImages `json:"images,omitempty"`
// contains filtered or unexported fields
}
Omit is the omit setting: what a request leaves out of the context though it is on the path, as format 0.11 records it, and, from 0.14, what it sends a placeholder for. Settings carry the object in force, which a config entry's Omit extends: its Reasoning replaces the rule in force when it names one, its Items are added to the set in force, its Outputs add their items to those in force and set the text, its Images replace the rule in force, and an object naming none of them clears all of them.
func (Omit) IsZero ¶ added in v0.0.20
IsZero reports whether the object names no rule, no entry and no placeholder, which is the object a config entry writes to clear the one in force, and the settings of a path that left nothing out. An empty id names no entry, so a list of them names none. An object read with an outputs or images member in a form the format does not define carries it, and is not zero.
func (*Omit) UnmarshalJSON ¶ added in v0.0.31
UnmarshalJSON takes reasoning and items as the struct names them, and outputs and images only in the form the format defines; a member in another form, which a file from before 0.14 was free to write, is left for the entry to keep as written.
type OmitImages ¶ added in v0.0.31
type OmitImages struct {
Keep int `json:"keep"`
Before string `json:"before"`
Placeholder string `json:"placeholder"`
}
OmitImages is the images member of omit, in one of two forms: with Before empty, every input_image part the request's item entries contribute but the last Keep is sent as an input_text part holding Placeholder; with Before set, every input_image part of an item entry on the path before the entry Before names is. A later images member replaces the one in force.
func (OmitImages) MarshalJSON ¶ added in v0.0.31
func (o OmitImages) MarshalJSON() ([]byte, error)
MarshalJSON writes the form the member is in: before, or keep.
func (*OmitImages) UnmarshalJSON ¶ added in v0.0.31
func (o *OmitImages) UnmarshalJSON(data []byte) error
UnmarshalJSON takes either form, spelled exactly: a non-empty string before or a non-negative integer keep, not both, beside a string placeholder. Anything else is an error, so an Omit holding it keeps the member as written.
type OmitOutputs ¶ added in v0.0.31
OmitOutputs is the outputs member of omit: each item entry on the path whose ID Items lists, and that holds a function call output, is sent with its output the string Text, its call ID and every other member as they are. The items in force are the union of every list since the last replace or compaction's checkpoint, as for Omit.Items, and the text is the one the latest member wrote.
type OmittedItem ¶ added in v0.0.20
type OmittedItem struct {
// Entry is the item entry whose item contributes nothing.
Entry *ItemEntry
// Reason is [OmitOtherModels] when the entry holds a reasoning item
// written under another model than the request's, and [OmitItems]
// when the omit in force lists it. An entry both rules reach is
// reported as listed.
Reason string
}
OmittedItem is an item entry a request leaves out of the context, and why.
type OmittedPart ¶ added in v0.0.6
type OmittedPart struct {
// ID names the part. It is empty on a keep, and on an element in
// force that names no part: see [OmittedPart.Unresolved].
ID string `json:"id,omitempty"`
Reason string `json:"reason,omitempty"`
Size int `json:"size,omitempty"`
Source string `json:"source,omitempty"`
// Keep, on a config delta, stands for the next Keep parts of the
// list in force, unchanged, and is the element's only member: see
// [Settings.OmittedDelta]. A keep member that is not a positive
// integer, which a file from before the member was defined may
// hold, is kept as written and Keep is zero; on an element that
// names an ID it means nothing.
Keep int `json:"keep,omitempty"`
// Of, on a keep, names a config entry earlier on the path: the keep
// counts over the list that entry put in force instead of the list
// in force before this entry, as format 0.11 lets a writer name the
// list an earlier entry wrote. It is the id of that entry. It means
// nothing beside an ID or without a Keep, and an of member that is
// not a non-empty string, which a file from before the member was
// defined may hold, is kept as written and Of is empty. See
// [Settings.OmittedDelta].
Of string `json:"of,omitempty"`
}
OmittedPart is a part the writer considered for the instructions and left out: a file the budget did not reach, a memory entry that did not fit, a skill out of scope. Reason is the writer's own word for why, Size the bytes the part would have added.
func (*OmittedPart) UnmarshalJSON ¶ added in v0.0.12
func (p *OmittedPart) UnmarshalJSON(data []byte) error
UnmarshalJSON decodes the part, taking keep only when it is a positive integer written as digits and of only when it is a non-empty string, so an earlier file's member in any other form stays a member this package does not define.
func (OmittedPart) Unresolved ¶ added in v0.0.12
func (p OmittedPart) Unresolved() bool
Unresolved reports whether the element names no part: a keep the path could not satisfy, left in the list in force as written, or an element with neither an ID nor a keep.
type Outcome ¶ added in v0.0.8
type Outcome int
Outcome is what an append did, which the format has a store report.
const ( // Continued: the entry was added under the leaf and is the leaf. Continued Outcome = iota // Branched: the entry was added elsewhere and the leaf did not move. Branched // Held: the session already held the entry; nothing changed. Held // LeafMoved: a leaf label moved the leaf to its target. LeafMoved // LeafNotMoved: a leaf label named a target the leaf may not rest // on, and was added without moving it. LeafNotMoved )
type OutcomeEntry ¶
type OutcomeEntry struct {
EntryBase `json:"-"`
Kind string `json:"kind"`
Target string `json:"target,omitempty"`
Score *float64 `json:"score,omitempty"`
Pass *bool `json:"pass,omitempty"`
Label string `json:"label,omitempty"`
Details json.RawMessage `json:"details,omitempty"`
}
OutcomeEntry is a judgement of how the session, or a range of it, went. Target names an entry in this session, usually the last entry of the range judged; a task or test name belongs in Details. Score is any finite number on the judge's own scale, named by Label; Pass is the judge's verdict when it has one.
func NewOutcomeEntry ¶
func NewOutcomeEntry(kind, target string) *OutcomeEntry
NewOutcomeEntry builds an outcome entry of the given kind about target, which may be "" for the session as a whole.
func (*OutcomeEntry) EntryType ¶
func (*OutcomeEntry) EntryType() string
EntryType returns "outcome".
func (*OutcomeEntry) MarshalJSON ¶
func (e *OutcomeEntry) MarshalJSON() ([]byte, error)
MarshalJSON emits the entry as one JSON object.
func (*OutcomeEntry) UnmarshalJSON ¶
func (e *OutcomeEntry) UnmarshalJSON(data []byte) error
UnmarshalJSON decodes the entry.
func (*OutcomeEntry) WithDetails ¶
func (e *OutcomeEntry) WithDetails(v any) *OutcomeEntry
WithDetails encodes v as the details and returns the entry, for chaining. An encoding failure leaves Details unset and is returned by the next Session.Append through MarshalEntry only if v cannot be marshalled at all, so callers should pass plain data.
func (*OutcomeEntry) WithLabel ¶
func (e *OutcomeEntry) WithLabel(label string) *OutcomeEntry
WithLabel sets the label and returns the entry, for chaining.
func (*OutcomeEntry) WithPass ¶ added in v0.0.5
func (e *OutcomeEntry) WithPass(pass bool) *OutcomeEntry
WithPass sets the judge's verdict and returns the entry, for chaining.
func (*OutcomeEntry) WithScore ¶
func (e *OutcomeEntry) WithScore(score float64) *OutcomeEntry
WithScore sets the score and returns the entry, for chaining.
type QueuedEntry ¶ added in v0.0.6
type QueuedEntry struct {
EntryBase `json:"-"`
// Item is the input as it will be appended.
Item openresponses.Item `json:"item"`
// Mode is ModeSteer or ModeFollowUp.
Mode string `json:"mode"`
// Trigger is what brought the input in.
Trigger *Trigger `json:"trigger,omitempty"`
// Ref names the queued input in the harness's own terms, for a
// caller that holds a handle to it.
Ref string `json:"ref,omitempty"`
}
QueuedEntry records an input a harness accepted before it could be appended to the conversation: a steer typed while the model is running, or a follow-up that waits for the run to end. It is a record entry, so the context algorithm ignores it and the item it holds reaches no request until it is appended as an ItemEntry.
A queued entry with no item entry naming it and no run end after it on the path is an input the harness still owes the conversation; Session.PendingQueued lists them, which is what a gateway that answered 202 drains on resume. The entry is where the trigger of that input lives, since the run entry names the trigger of the run and an input that joins a run in flight has a different one.
func NewQueued ¶ added in v0.0.6
func NewQueued(item openresponses.Item, mode string) *QueuedEntry
NewQueued builds a queued entry for an input accepted in mode ModeSteer or ModeFollowUp.
func Queued ¶ added in v0.0.6
func Queued(path []Entry) []*QueuedEntry
Queued returns the queued entries on a root-first path that are still waiting: no item entry on the path names them in QueuedFrom, and no run end follows them. They are the inputs a harness accepted and has not yet appended to the conversation, the durable inbox a resume drains. A run end after a queued entry closes it, since the run it was queued into has ended: a harness that still wants the input queues it again.
func (*QueuedEntry) Drain ¶ added in v0.0.6
func (e *QueuedEntry) Drain() *ItemEntry
Drain returns the item entry that appends this queued input to the conversation: the item, the trigger as its source and QueuedFrom naming this entry, so the record says why the item is there. The entry must already have an ID, which it has once it is appended. The trigger is copied whole, the members the format does not define included, so the two entries do not share one once both are appended and neither may be modified.
func (*QueuedEntry) EntryType ¶ added in v0.0.6
func (*QueuedEntry) EntryType() string
EntryType returns "queued".
func (*QueuedEntry) MarshalJSON ¶ added in v0.0.6
func (e *QueuedEntry) MarshalJSON() ([]byte, error)
MarshalJSON emits the entry as one JSON object.
func (*QueuedEntry) UnmarshalJSON ¶ added in v0.0.6
func (e *QueuedEntry) UnmarshalJSON(data []byte) error
UnmarshalJSON decodes the entry, dispatching the item through the openresponses item registry.
func (*QueuedEntry) WithTrigger ¶ added in v0.0.6
func (e *QueuedEntry) WithTrigger(kind, ref, source string) *QueuedEntry
WithTrigger records what brought the input in and returns the entry, for chaining.
type RawEntry ¶ added in v0.0.18
type RawEntry struct {
ID, Parent, Type string
// Line is the line as read, without its terminator.
Line []byte
// Repeat is set for a line whose id an earlier line had, which a
// reader treats as that same entry.
Repeat bool
// contains filtered or unexported fields
}
RawEntry is one entry line as Scan met it: its envelope, read from the line's own bytes, and the line, which Decode turns into an entry when asked.
type Reader ¶ added in v0.0.19
Reader is implemented by a store that can read a session without holding it. Read takes no hold, writes nothing, the recovery an Open would write included, and keeps nothing: it returns a session of its own, read from what the store holds, which no later append reaches and whose leaf moves move no other session's. A session this store or another process is writing is read as a store opened read-only would read it, and stays the writer's. A missing session is ErrNoSession.
What Read sees of a session being written is what the store held when it read: an append in flight may not be there yet, and a leaf a writer moved through Session.Branch and has not recorded is not. Nor need what it sees be durable: Read can show an append its writer has not yet made durable, under a lazy sync policy or while its fsync runs, which a crash can take back. Each store says what, if anything, reads only what is durable.
type RedactedFrom ¶ added in v0.0.27
type RedactedFrom struct {
Session string `json:"session"`
Profile string `json:"profile,omitempty"`
// Extra holds redacted_from members this package does not define.
Extra map[string]json.RawMessage `json:"-"`
}
RedactedFrom is a redacted session's link to its original, the header's redacted_from member. Session is the original's ID. Profile names the redaction, its kind and its rules at a version, so two redactions of one original are told apart; a redaction that writes one derives the session's ID with RedactedSessionID. Which entries of the original each redacted entry was made from is the entry's own redacted member (RedactedOf), so the object pins no version: the session grows as the original does. Extra holds the members this package does not define, the namespaced ones a redaction records about itself among them, and a profile that is not a non-empty string, each kept as written. The entries and aligned members of an earlier draft of the format are members this package does not define, and are kept there too.
func (RedactedFrom) MarshalJSON ¶ added in v0.0.27
func (r RedactedFrom) MarshalJSON() ([]byte, error)
MarshalJSON emits the object with Extra flattened into it. A member of Extra that a typed field also writes is refused, since the object would hold it twice.
func (*RedactedFrom) UnmarshalJSON ¶ added in v0.0.27
func (r *RedactedFrom) UnmarshalJSON(data []byte) error
UnmarshalJSON decodes the object, matching members by their exact names. It is refused unless session is a non-empty string; a profile that is not a non-empty string stays in Extra with the members this package does not define, so the object writes back as it was read.
type RefMovedError ¶ added in v0.0.21
type RefMovedError struct {
Name string
// Current is the target the ref holds, the zero RefTarget when it
// holds none.
Current RefTarget
}
RefMovedError is the error UpdateRef returns when the ref holds another target than the one expected. errors.Is(err, ErrRefMoved) holds, and errors.As finds the holder's target.
func (*RefMovedError) Error ¶ added in v0.0.21
func (e *RefMovedError) Error() string
Error implements error.
func (*RefMovedError) Unwrap ¶ added in v0.0.21
func (e *RefMovedError) Unwrap() error
Unwrap returns ErrRefMoved.
type RefStore ¶ added in v0.0.21
type RefStore interface {
// ResolveRef returns the ref's target, or [ErrNoRef]. A ref whose
// session is gone is dangling: ResolveRef returns its target
// together with an error wrapping [ErrNoSession], so a caller can
// tell which session it was and move the ref.
ResolveRef(ctx context.Context, name string) (RefTarget, error)
// UpdateRef moves the ref from expected to next, the zero RefTarget
// meaning none: none as expected creates the ref if it is absent,
// none as next deletes it. On a mismatch it returns [ErrRefMoved]
// as a [*RefMovedError] carrying the target the ref holds. A next
// whose session the store does not hold is [ErrNoSession], and one
// whose entry that session does not hold is [ErrNoEntry]. An update
// that changes nothing is not logged. reason is recorded in the ref
// log and may be empty.
UpdateRef(ctx context.Context, name string, expected, next RefTarget, reason string) error
// ListRefs lists the refs whose name begins with prefix, in name
// order. It does not check that the sessions exist.
ListRefs(ctx context.Context, prefix string) iter.Seq2[Ref, error]
// RefLog lists the ref's updates, newest first, and nothing for a
// name never updated. It outlives the ref's deletion.
RefLog(ctx context.Context, name string) iter.Seq2[RefUpdate, error]
}
RefStore is implemented by a store that keeps refs, as RFC 0002's refs section defines them. A store opened read-only resolves and lists, and reports ErrReadOnly from UpdateRef.
type RefTarget ¶ added in v0.0.21
type RefTarget struct {
Session string
// Entry is the entry the ref pins, or "". It must be an entry the
// session holds: its base, one of its own, or one on its prefix.
Entry string
}
RefTarget is what a ref points to: a session, and optionally an entry of it that the ref pins. The zero RefTarget is "no ref", as an expected value and as a next one.
type RefUpdate ¶ added in v0.0.21
RefUpdate is one accepted update, as the ref log records it. Old is the zero RefTarget for a creation and New is for a deletion.
type Reference ¶ added in v0.0.27
type Reference struct {
// At is the member, an RFC 6901 JSON Pointer into the entry or the
// header, such as /parent, /parents/1/entry or /omit/items/0.
At string
// Session is the session the reference names: a session reference's
// ID, or the session an entry reference names its entry in, "" for
// this file.
Session string
// Entry is the ID an entry reference names; empty for a session
// reference.
Entry string
}
Reference is one member naming an entry or a session that a Remap left as written.
type Releaser ¶ added in v0.0.31
Releaser is implemented by a store that holds the sessions it opens, as Store.Open describes: Release lets the hold on the session go, for every caller that opened it, and forgets the session, so the next Open reads it again. An error says the hold could not be let go; the session is forgotten all the same. Releasing a session the store does not hold is not an error. A program that serves a store, as RFC 0003's server does, releases a session through it when a lease ends.
type Remap ¶ added in v0.0.27
type Remap struct {
// Entry maps a reference to an entry: entry is the ID written, and
// session the session the reference names the entry in, "" when it
// names none, which is this file. Only a parents element names a
// session, and it may name this file's own session by its ID, which
// the callback takes as it takes "".
//
// An empty ID and true say the entry was left out and nothing a
// member may name stands in its place, and RemapRefs applies RFC
// 0001's rules for leaving entries out: a member naming one element
// of a set loses it (a parents element, an omit items or outputs
// items ID, an item's queued_from or from_link), a list so emptied
// is removed, and an omit left with no member with it; parent names
// the universal root, null, or Base in a file with one; a leaf
// label's target, the one other member that may, names the root;
// and any other member, which names one predecessor and must name
// what stands in the place, is refused with [ErrNoPlace].
Entry func(session, entry string) (string, bool)
// Bound maps a reference that bounds a range from below, a
// compaction's first_kept and images' before in a config or a
// checkpoint, which RFC 0001 gives a place of its own: a left-out
// entry is replaced there by the first entry written after it on its
// path, since naming one above it would put that entry back into
// every later request, or send its images. An empty ID and true say
// nothing was written after it, which is refused with [ErrNoPlace].
// When Bound is nil, Entry maps those members too, which is right
// only while every entry they name was kept: a writer that leaves
// entries out sets it.
Bound func(entry string) (string, bool)
// Base is, for entries written to a file with a base, that base as
// the file names it. Every entry given is then one of the session's
// own, and the base is above each on its path, so a parent with
// nothing else in its place names the base and not the universal
// root, which would break the base rule (RFC 0001, Leaving entries
// out). Empty for a file without one.
Base string
// Session maps a reference to a session by its ID.
Session func(id string) (string, bool)
}
Remap says where the references RemapRefs meets now point. A callback returns the ID a reference is to name instead, or false to leave it as written; a nil callback leaves every reference of its kind as written and reports none of them.
type Remapped ¶ added in v0.0.27
type Remapped struct {
// Entry is the entry with its references re-pointed: a new entry,
// whose ID, timestamp and every other member are the input's.
Entry Entry
// Unmapped lists the references a callback left as written, member
// by member.
Unmapped []Reference
// Dropped lists the references removed because the entry they named
// was left out, each at the place it had in the input.
Dropped []Reference
// Opaque is set for an extension entry. Its members are its own and
// this package cannot read them, so only its envelope, parent and
// parents, is re-pointed, and a member of its own may still name an
// entry or a session as written.
Opaque bool
}
Remapped is what RemapRefs made of an entry.
func RemapRefs ¶ added in v0.0.27
RemapRefs re-points every member of e that names an entry or a session, through m, and returns the result as a new entry; e is not changed. The entry references are parent, each parents element's entry, an item's queued_from and from_link, a compaction's first_kept and the omit items, outputs items and images before of its checkpoint, a branch summary's from, the target of a label, an outcome, a dispatch and a decision, a config entry's omit items, outputs items and images before and the of of each omitted part that is a keep, a positive keep and no id, and the target of a judged_by link and of a cancel notice, which name an entry of this session. The session references are each parents element's session and a link's session. A member of one of those names in a form or place the format does not define it in, kept as written by the decoder, such as an of beside an id, names nothing and is left alone, as is an item's response, which is a provider's ID and not an entry's, and a redacted member, which names entries of the original by position and not by id.
Every other member, kept extras and members the format does not define among them, comes through unchanged, and so does the order of parents unless an element of it was re-pointed: then the elements are sorted by session and entry, as a writer must write them, since the entry is a new one, an extension entry's as a core entry's. Its ID is the input's, which no longer hashes it, so a writer clears it before a commit, which would refuse it with ErrBadID, and the content hash is empty until the commit computes both. A writer that re-points parent here, rather than setting it afterwards, keeps a line the fields did not reproduce as the line, with the references changed.
What it cannot read, an extension entry's members, is reported as Opaque; a reference a callback left as written, in Unmapped. Header members are RemapHeaderRefs's. Which entry stands in the place of one a writer leaves out, a placeholder or the nearest entry kept above it, is the writer's to say, and the callback returns it; when nothing does, the callback returns an empty ID and RemapRefs removes or refuses each member that named the entry as RFC 0001's Leaving entries out has it, reporting what it removed in Dropped.
type ReplacedItem ¶ added in v0.0.31
type ReplacedItem struct {
// Entry is the item entry as recorded.
Entry *ItemEntry
// Reason is [OmitOutputsReason] for an output sent as the
// placeholder text, and [OmitImagesReason] for image parts sent as
// text parts.
Reason string
// Parts is the number of image parts replaced; zero for an output.
Parts int
}
ReplacedItem is an item entry a request sends with a placeholder in place of part of its item, and why.
type ResponseEntry ¶
type ResponseEntry struct {
EntryBase `json:"-"`
ResponseID string `json:"response_id"`
Model string `json:"model,omitempty"`
Status openresponses.ResponseStatus `json:"status"`
Usage *openresponses.Usage `json:"usage,omitempty"`
Incomplete *openresponses.IncompleteDetails `json:"incomplete,omitempty"`
Error *openresponses.ErrorPayload `json:"error,omitempty"`
RequestHash string `json:"request_hash,omitempty"`
LatencyMS int64 `json:"latency_ms,omitempty"`
// Attempts is the number of calls to the model this response took,
// itself included, when the harness retried calls that failed and
// recorded none of them as a response entry of its own. Zero means
// one. An attempts member that is not a positive integer, which a
// file from before the member was defined may hold, is kept as
// written in Unknown and Attempts is zero.
Attempts int `json:"-" member:"attempts"`
}
ResponseEntry is the envelope of one model call, written after its output items. It is not in context.
func (*ResponseEntry) Calls ¶ added in v0.0.10
func (e *ResponseEntry) Calls() int
Calls returns the number of calls to the model the response took: Attempts, or one when it is not set.
func (*ResponseEntry) EntryType ¶
func (*ResponseEntry) EntryType() string
EntryType returns "response".
func (*ResponseEntry) MarshalJSON ¶
func (e *ResponseEntry) MarshalJSON() ([]byte, error)
MarshalJSON emits the entry as one JSON object.
func (*ResponseEntry) UnmarshalJSON ¶
func (e *ResponseEntry) UnmarshalJSON(data []byte) error
UnmarshalJSON decodes the entry.
type Result ¶ added in v0.0.8
type Result struct {
ID string
Outcome Outcome
// Unresolved lists references in the entry a store could not
// resolve — a sidecar blob it does not hold, a convergence into a
// session it does not have — which the format lets a store report
// rather than refuse. A session in memory sets nothing here.
Unresolved []string
// Reopen is set by a store when the append is durable but the
// caller's session could not be brought in step with it, so the
// caller opens the session again before using it further.
Reopen bool
// Durable is set by a store that says whether it acknowledged the
// append once it was durable, as RFC 0002 asks of a store that
// may acknowledge one before. A session in memory, and a store
// that does not report it, leaves it false.
Durable bool
}
Result is what Commit reports: the entry's ID and what appending it did. A store adds what it alone can know.
type Rewriter ¶ added in v0.0.27
type Rewriter struct {
// contains filtered or unexported fields
}
Rewriter is RewriteStrings over the entries of one session file, given to it in file order, prefix included: one Rewriter per file. It carries what one entry cannot see. An instruction part a delta does not repeat is named by the hash of its text, which an earlier entry holds, so the Rewriter remembers what each text it rewrote hashes to now, and a later part naming it by hash names the rewritten text. And instructions beside parts named by hash or keep are their join only as the path resolves the parts, so the Rewriter replays the settings along each entry's path as context building does, and rebuilds such instructions from the rewritten parts.
A part named by a hash whose text the Rewriter has not met keeps its hash, which then names the original's text, and RFC 0001 forbids a redactor to write it: Rewriter.Unmet reports each such part, so a redactor writes it as text of its own or refuses before it commits the entry. What it has met depends on what it was given, so a Rewriter fed another file's entries, or a file's out of order, can rewrite one entry two ways: a fork and its origin each redacted by a Rewriter of their own give their shared prefix the same bytes. A Rewriter is not safe for concurrent use. The zero value is not usable; see NewRewriter.
func NewRewriter ¶ added in v0.0.27
NewRewriter returns a Rewriter applying f, under the contract RewriteStrings states.
func (*Rewriter) LeftOut ¶ added in v0.0.31
LeftOut tells the Rewriter that the redactor leaves e out of what it writes. A redactor gives the Rewriter every entry of the file, in file order: each it writes to Rewrite, and each it leaves out here, in place of Rewrite or after it. The entries under e then read the settings in force above it, as the redacted path does, so a part whose text only e put in force, or a keep counting parts e put in force, is reported by Unmet.
func (*Rewriter) Rewrite ¶ added in v0.0.27
Rewrite is RewriteStrings with what the entries the Rewriter was given before, earlier in the file, say about this one: the rewritten hashes of the instruction parts they hold, and the parts in force on its path.
func (*Rewriter) Unmet ¶ added in v0.0.31
Unmet returns, in the order met, every part the Rewriter has written that its path cannot rebuild (see UnmetPart). A redactor reads it after each Rewriter.Rewrite, before it commits the entry; an entry's parts are added to it by the Rewrite that wrote them.
type Run ¶ added in v0.0.5
type Run struct {
Start *RunEntry
// End is nil when no end entry for the run is on the segment.
End *RunEntry
Segment []Entry
// Path is the root-first path up to the last entry of the segment,
// of which Segment is the tail. The end reason of a run that
// answers a call an earlier run's model call made is a shape of
// the path, not of the segment alone; see [ComputeReason]. It is
// nil in a Run built by hand, and then the segment stands for the
// path.
Path []Entry
}
Run is one run's segment of a path: the entries from its start entry to its end entry, or to the end of the path when the run was cut off or closed by a branch.
func Runs ¶ added in v0.0.5
Runs partitions a root-first path into its runs. Entries before the first start entry belong to no run and are dropped; a start entry closes any run still open, as a branch does. Only an end entry whose run_id matches the open run closes it.
func (*Run) Calls ¶ added in v0.0.5
Calls returns the run's calls: those on its segment, and those made before it that the segment holds a decision, a dispatch or an output for, since the run took them up. Each carries what the path holds for it; see Calls.
func (*Run) Empty ¶ added in v0.0.19
Empty reports whether the run's segment, between its start and its end, or after its start for a run that was cut, holds no entry in context (an item, a config, a compaction or a branch summary), no response and no dispatch or decision: a run refused, or killed, before it took anything up, though a writer may have replayed a link onto its branch or a hook annotated it. Such a run written SourceResume is the shape of SourceInput, and the format accepts it as written, since all it shows is that its writer meant to take up a call and took up nothing: Session.VerifyRecords does not report it with ErrSourceMismatch. Format 0.11 accepted a segment holding nothing; 0.14 accepts record entries in it, in a file of any minor. Empty tells it from a resume that adds a message, a setting or an output and takes up nothing, which is still reported, and from one that called the model and took nothing up, which is a retry's shape, an input's.
func (*Run) Pending ¶ added in v0.0.5
Pending returns the IDs of the run's calls with no output on the path, a call made before the segment that the run took up among them; see Run.Calls.
func (*Run) Verify ¶ added in v0.0.5
Verify checks the run's end entry against its segment. A written error or interrupted stands over any segment; any other reason must match ComputeReason, and the pending list must match Run.Pending. A run without an end verifies trivially.
type RunEntry ¶ added in v0.0.5
type RunEntry struct {
EntryBase `json:"-"`
RunID string `json:"run_id"`
Phase string `json:"phase"`
// Source is set on a start entry.
Source string `json:"source,omitempty"`
// Reason is set on an end entry.
Reason string `json:"reason,omitempty"`
// Ref names the trigger of a start or the cause of an end in the
// harness's own terms. Readers treat it as opaque.
Ref string `json:"ref,omitempty"`
// Trigger, on a start entry, says how the input that started the run
// arrived, in parts, beside Ref and changing nothing about it. A
// harness's richer facts about the firing, such as when it was due
// or which attempt it is, go in [Trigger.Unknown]; members of the
// run entry this package does not define, where format 0.6 put
// them, are kept in [EntryBase.Unknown]. A trigger member this type
// cannot hold exactly, one written before the member was defined,
// is kept there as written and Trigger is nil.
Trigger *Trigger `json:"-" member:"trigger"`
// Pending lists, on an end entry, the IDs of the calls left without
// an output. It is written even when empty.
Pending []string `json:"pending"`
}
RunEntry marks the start or the end of one pass of the harness's loop. Two entries per run share a RunID. A start carries Source and an optional Ref naming what triggered the input in the harness's own terms; an end carries Reason, the IDs of the calls left pending and an optional Ref naming the cause.
func NewRunEnd ¶ added in v0.0.5
NewRunEnd builds the entry that closes a run. pending lists the IDs of the calls left without an output; Session.EndRun computes it from the path. ref may be "".
func NewRunStart ¶ added in v0.0.5
NewRunStart builds the entry that opens a run. ref may be "".
func (*RunEntry) MarshalJSON ¶ added in v0.0.5
MarshalJSON emits the entry as one JSON object. Which members are written depends on the phase: a start carries source, an end carries reason and always carries pending, even when empty, since a run that ended owing nothing is a fact a reader relies on.
The other phase's members are written when they are set rather than dropped. Nothing this library builds sets them, and validateLifecycle asks only for the phase's own, so a well-formed entry is written exactly as it was before. A file from elsewhere that carries one is the case that matters: the envelope rule is that a reader preserves what it does not itself need, and rewriting such a file to drop a member it declared breaks that promise silently. Rejecting the shape on the way in would be the alternative, and a larger decision than an encoder.
func (*RunEntry) UnmarshalJSON ¶ added in v0.0.5
UnmarshalJSON decodes the entry.
type Session ¶
type Session struct {
// contains filtered or unexported fields
}
Session is one session in memory: the header, the entries in file order, the tree they form and the current leaf. It is safe for concurrent use. Entries are shared, not copied, and must not be modified after they are appended.
func Continue ¶ added in v0.0.5
func Continue(ctx context.Context, store Store, id string, summary openresponses.Item) (*Session, error)
Continue rolls a session over into a successor, for a conversation that has outgrown its file: a months-long unattended session, or one whose compaction has folded the context many times over while the file kept growing. It does the four steps in the order the format wants them: the successor is created with parent_session naming id and the old header's harness, working directory, records and media mode; its first entry is a full config carrying the settings in force at the old leaf, so the first request on its first entry rebuilds and a recorder finds a config on the path; summary, when not nil, is appended as the item that carries the conversation across; the old session's display name is carried over; and the old session gets a continued_in link naming the successor, which is what a store reads to mark it superseded. It returns the successor.
A session continues once (RFC 0002, Refs and continuation): one that already holds a continued_in link is refused with ErrSuperseded, since a second successor would fork the chain. The store refuses the link itself under the lock it appends under, so of two writers continuing at once one lands its link and the other is refused, its successor deleted. A caller continuing a named conversation uses ContinueRef, which continues the session at the end of the chain.
func ContinueRef ¶ added in v0.0.21
func ContinueRef(ctx context.Context, st Store, name string, summary openresponses.Item) (*Session, error)
ContinueRef continues the session that is current for ref name, as Continue does, and moves the ref from its target to the successor by compare-and-swap, so a named conversation that rolls over keeps its name. The session continued is the end of the continued_in chain from the ref's target, as ResolveCurrent finds it, so a ref left behind by a writer that continued its session and stopped before it moved the ref is caught up rather than forking the chain; the compare is against the ref's literal target. The ref's pinned entry, which names an entry of the old session, is not carried.
When the ref moved while the continuation ran, the error is ErrRefMoved and the successor is returned with it: it exists, the old session is retired, and the caller decides where the name goes.
func Fork ¶ added in v0.0.8
Fork creates a session that continues from entry at of origin: a session with a base, in the format's terms. The new session's header is h with Base set to at and, when h names none, ParentSession set to origin's ID; its entries open with origin's path to at, the prefix, which the new session shares with origin rather than copies, and its leaf is the base. Every entry it appends hangs from the base or from an entry it appended itself, and its file opens with the prefix so it stands alone. The base may not be a leaf label, since it is the fork's first leaf and the leaf never rests on one, and h's media, when set, must be origin's, since the prefix was written in that form. Fork refuses a prefix that could not stand alone: one holding an entry that converges an entry of origin off the path (ErrBadConvergence), or an entry of a migrated origin that the migration could not rewrite (ErrUnresolvedMigration). It refuses a fork written at 0.13 or later whose prefix holds an entry with a redacted member read under a minor before 0.13 (ErrNoRaise), as Session.RaiseRefused says, since the fork's format would give the member a meaning it was not written with, and, the other way round, a fork written under a minor before 0.13 whose prefix holds a redacted entry (ErrUnsupportedFormat).
A store's Create does the same for a header whose Base is set, so Fork is the in-memory form and a store's Create the persistent one. The memory, jsonl and sqlite stores fork through it. cas reads the prefix under the new header, so it asks Fork of the session that holds the base, owner or prefix holder, read under its own header, when that session's header or the fork's names a minor before 0.13, and refuses when it cannot read it: every store refuses the same forks over a redacted member written under another minor.
func ImportMedia ¶ added in v0.0.31
func ImportMedia(ctx context.Context, st Importer, r io.Reader, media fs.FS, asRecord bool) (*Session, []string, error)
ImportMedia imports an RFC 0001 file into st with the session's sidecar directory, media, as RFC 0002's import of a projection does: it verifies each blob the session's items name against its name and stores it, and returns the hashes of those the directory lacks and the store does not hold, unresolved, as an append reports one. The session is imported either way; one with an unresolved blob cannot be pushed or projected until the blob arrives. A blob that fails its name refuses the import with ErrBlobMismatch. A session whose media is not sidecar is imported as Importer.Import does, and media is not read.
A store that implements MediaImporter does the whole of it. For one that does not, a sidecar session needs a BlobWriter, or it is refused with ErrNoBlobWriter; the file is imported first and the blobs stored after, so a refused import stores nothing, and a failure between the two leaves the blobs unresolved, which another call to PutBlob resolves. Where the store is no BlobReader either, a blob the directory lacks is reported unresolved though the store may hold it.
func New ¶
New creates an empty session. Header fields left empty are filled: a UUIDv7 ID, the current time, this package's format and payload. Times the session assigns are in UTC so a file carries one offset however the writer's clock is configured; times the caller supplies are kept as given.
func Original ¶ added in v0.0.27
Original returns the session s was redacted from, which s's header names in redacted_from, as st holds it, read through Reader where st implements it, so no hold is taken, and opened otherwise; and the entries each of s's redacted entries was made from. A store that does not hold the original reports ErrNoSession, as does a redacted session that names no original, as one written before 0.13 may; a session that is not redacted is ErrNotRedacted.
The mapping takes each redacted entry's ID to the IDs of the original's entries its positions name, ranges expanded, in the order they are named, by position in the original's FileOrder counting from 1. An entry a writer appended natively, which carries no redacted, and an entry the redaction added, which carries [], are made from nothing in the original and are not in the mapping; RedactedOf tells them apart. A position past the entries the store holds of the original is ErrStaleOriginal: its copy has not reached the version redacted.
A position counts in the original of the session whose own entry it is (RFC 0001, Redacted entries). For s a fork, its prefix is another session's own entries: those are resolved in the original that session's redacted_from names, following parent_session to the session that owns them, where st holds it and it is redacted, and are left out of the mapping where it does not. The session returned is s's own original.
func Read ¶
Read decodes a session from its JSONL form. A final line that does not parse is tolerated and reported through Session.Truncated; any other malformed line, a missing parent, a convergence reference that names no entry yet in this file, a line that breaks the base rule (ErrBaseRule) in a file whose header names one, or a repeated ID is an error. The leaf is the last entry in the file unless a LeafLabel is in force, in which case it is the entry that label names.
Each line is read under the format the header declares, and the header is brought up to this package's format in memory unless the file may not be raised (ErrNoRaise): then it keeps its own, or for a file before 0.5, whose entries are rehashed as 0.5's, 0.12. Before 0.13 an entry's redacted member is unknown whatever its form (RedactedOf).
func ReadForImport ¶ added in v0.0.27
ReadForImport reads an RFC 0001 file a store is to import and holds it to the checks of RFC 0002's import that need nothing of the store: Read's, every line verified and a header with redacted_from without redacted refused among them, and then a last line cut short (a *TruncatedLine), a migration from before 0.5 that left an entry unresolved, an own entry above the base (ErrBaseRule), and a synthetic leaf marker (ErrSyntheticMarker) are refused. A store then checks the session ID, the media against a held session whose own entries include the base, and its own settings, and stores the session's entries in file order: the prefix, which joins no session's own entries, then its own.
func SessionFor ¶ added in v0.0.21
SessionFor returns the session ref name points to, creating one from h and setting the ref when there is none. It is the answer to two harnesses that each create a session for one name: the ref is created by compare-and-swap, so one caller's session wins, and the other opens the winner's and deletes its own, which nothing else has seen. A dangling ref, whose session is gone, is replaced the same way.
The session comes from Open, so a store that guards sessions against a second writing process reports ErrSessionLocked to a caller whose winner is held by another process.
func (*Session) Append ¶
Append adds e to the tree. Its ID is the hash of its envelope over the hash of its body, as the format defines, and is computed here; an ID the caller set must match or the append is refused. An empty Parent is set to the current leaf, so an explicit Parent branches in place, unless the caller set the ID: an entry whose ID is set and whose Parent is empty hangs from the universal root, a branch there that continues only when the leaf is the root, as RFC 0002's Append has it; a zero Timestamp is set to now, and any timestamp is taken to UTC, the one spelling the format admits. In a session with a base, the parent must be the base or an own entry.
An append under the leaf makes the entry the leaf; an append elsewhere is a branch and the leaf does not move. A leaf label makes its target the leaf, wherever the label's own parent sits, when the target is the base or an own entry and not itself a label; otherwise nothing moves. The leaf never rests on a leaf label. An entry the session already holds — same type, body, parent, parents and timestamp — is a no-op that returns the existing ID.
Parents, when the caller set any, is sorted into the order the format requires and checked against the convergence rules; it does not move the leaf and does not reach any context. On success e is owned by the session and must not be modified.
func (*Session) Branch ¶
Branch moves the leaf to id, so the next append becomes a child of that entry. When id is inside a run, the run is open on the new path; see Session.EndRun for who closes it.
func (*Session) Callees ¶ added in v0.0.24
func (s *Session) Callees(leaf string) ([]CalleeLink, error)
Callees returns the callees on the path to leaf; see Callees.
func (*Session) Calls ¶ added in v0.0.5
Calls returns the calls on the path to leaf; see Calls. A call made in a fork's prefix is known as one, so its State reads the header's promise as not covering it.
func (*Session) Children ¶
Children returns the IDs of the entries whose parent is id, in file order. Pass "" for the roots.
func (*Session) Compact ¶
func (s *Session) Compact(firstKept string, summary openresponses.Item) (*CompactionEntry, error)
Compact builds the compaction entry for the current leaf: FirstKept names the earliest entry on the path that stays in context, summary is the item that replaces everything before it, and the settings checkpoint is taken from the context at the leaf. The entry is not appended; set TokensBefore, Usage or Pinned if they apply, then append it through the store. A caller that knows an item index rather than an entry ID uses Session.CompactFrom or Session.CompactKeeping.
func (*Session) CompactFrom ¶ added in v0.0.4
func (s *Session) CompactFrom(first int, summary openresponses.Item) (*CompactionEntry, error)
CompactFrom is Session.Compact for a caller that split the request input at an index: FirstKept is the entry that contributed item first of the context at the leaf, and the items before it are what summary replaces. The index counts the items the context algorithm produces, Context.Items, so entries that contribute no item do not shift it. The item at first cannot be an earlier compaction's summary or one of its pinned items: the format keeps entries, and what a compaction contributes is kept only while it is the last compaction on the path.
func (*Session) CompactKeeping ¶ added in v0.0.4
func (s *Session) CompactKeeping(kept int, summary openresponses.Item) (*CompactionEntry, error)
CompactKeeping is Session.Compact for a caller that knows how many items of the request input it kept: the last kept items of the context at the leaf stay, and summary replaces the rest. kept must be at least 1, because FirstKept names an entry, and at most the number of items in the context.
func (*Session) ContextAt ¶
ContextAt builds the context at entry id, the request a model call appended after that entry would receive. An empty id yields an empty context.
func (*Session) ContextHash ¶ added in v0.0.8
ContextHash computes the context hash at entry id, as RFC 0002 defines it: the key a store can compute as it appends and a router can key a provider's cached prefix on. It is defined over the path ending at the entry by the entry's type and not by any later leaf. An item, config, compaction or branch_summary contributes; a response, a record entry and an extension entry do not. The value before any contributing entry is the hash of the canonical null; a non-contributing entry takes its parent's; a contributing entry hashes the three-element array of its parent's context hash, its type and its content hash as the section defines, with the per-run provenance members removed from the body first and a compaction's first_kept replaced by the context hash and type of the entry it names.
It excludes ts and parents, it is incremental, and two sessions whose contributing entries are byte-identical share it. It is not request_hash: on a path with no compaction the two identify the same request, and after a fold they part.
func (*Session) DeclaredFormat ¶ added in v0.0.18
DeclaredFormat returns the format the session's file named in its header when it was read, before Read brought the header up to the format this package writes; for a session made here, the format it writes. A reader that hedges on a rule a minor gained says so of the minor the file declared. A store that raises the header before an append leaves this as read; the session read again declares the raised format.
func (*Session) Dispatches ¶ added in v0.0.18
func (s *Session) Dispatches(target string) []*DispatchEntry
Dispatches returns the dispatches for the call whose function call is the entry target, anywhere in the session, in the order they were added, each naming the call by its call ID, a withdrawn one among them: a withdrawal says its dispatch was no hand-off on the path it is on, and another branch may have handed the call off from the same dispatch. A call with none on its path may have one elsewhere, which a rebase above it leaves, and may then have run. A call in a fork's prefix has its dispatches, if any, in the session the fork was made from, which the fork does not hold; OriginDispatches reads them through a store.
func (*Session) EndRun ¶ added in v0.0.5
EndRun builds the end entry for the run open at the current leaf: its pending list is Run.Pending. reason is one of the Reason constants and ref may be "". The entry is not appended.
A writer that continues a path on which a run is open that it is not running owns that run and closes it before appending anything else, as the format has it: after Session.Branch into a run, with ReasonInterrupted and a ref naming the rewind; on resuming a run a crash cut off, with ReasonError and a ref naming the cut. Session.OpenRun at the leaf says whether there is one.
func (*Session) Entries ¶
Entries returns the entries in file order. The slice is a copy; the entries are shared.
func (*Session) Extend ¶ added in v0.0.21
Extend adds e, an entry a store already holds, to a session read from that store, and places the leaf as Read would had e been in what it read: the newest entry under the durable leaf mark that was appended after it, the mark itself when none was, and the newest entry when no mark is in force. Session.Commit applies the live rule instead, under which an entry whose parent is not the leaf is a branch and moves nothing; a session read from a file has no live leaf to follow, so the two differ after a writer's Session.Branch. A follower of a store whose sessions Read builds keeps its session up to date with Extend.
Extend reads e under the format the session's file declared (Session.DeclaredFormat), as Read reads each line: before 0.13 an entry's redacted member is one this package does not know, so a typed one goes back to Unknown as written and e is not redacted (RedactedOf), and the session then holds a file a writer may not raise (ErrNoRaise).
func (*Session) HeadLabel ¶ added in v0.0.31
func (s *Session) HeadLabel(expected, to string) (*LabelEntry, error)
HeadLabel is the compare of a head compare-and-swap (HeadStore) for a store that moves a session's head by appending a leaf label: it checks that the leaf is expected and that the head may rest on to, and returns the label whose append moves the leaf there, hung from to as Session.MarkLeaf's is. Either may be "", the universal root, the empty context: a label to the root has a null target. A leaf that is not expected is ErrHeadMoved; a target the session does not hold, holds on the prefix above its base, or the root in a session with a base, is ErrNoEntry; and a leaf label is ErrHeadTarget. The store appends the label under the same lock it read the leaf under, so nothing moves the leaf between the two.
func (*Session) Judges ¶ added in v0.0.20
Judges returns the judged_by links on the path to leaf; see Judges.
func (*Session) Labels ¶
Labels returns the current label of every labelled entry: the last label entry per target wins, and a null label clears.
func (*Session) Leaf ¶
Leaf returns the ID of the entry the next append will name as its parent, or "" when the head is the universal root, the empty context every session's first entries hang from, and the next append is a first entry.
func (*Session) LeafMarkBefore ¶ added in v0.0.31
func (s *Session) LeafMarkBefore(e Entry) *LabelEntry
LeafMarkBefore is Session.LeafMarkNeeded decided before e, an entry Session.Prepare has filled, is appended: the label that, appended first, keeps a reader of the lines on the session's head once e follows it, or nil when e needs none before it. A store of lines writes the label ahead of e, so a crash that tears e off the end of the file leaves the label, never e without it. An entry that descends from the head is the one case a label ahead cannot cover, since a reader takes the newest line under the label after it; the store asks Session.LeafMarkNeeded after such an entry.
func (*Session) LeafMarkNeeded ¶ added in v0.0.31
func (s *Session) LeafMarkNeeded() *LabelEntry
LeafMarkNeeded returns the leaf label a store that keeps the session as RFC 0001 lines appends after an append, so that a reader of those lines resolves the head the session has, or nil when they already do. A reader takes the newest entry under the leaf label in force, or the last line, so a branch, an entry appended away from the head, would read back as the head, which RFC 0002 says does not move; the label keeps it where it is, the universal root among the places it may be, which a label with a null target names. The label hangs from the leaf, as Session.MarkLeaf's does.
func (*Session) Leaves ¶
Leaves returns the IDs of the entries that have no children, in file order.
func (*Session) MarkLeaf ¶ added in v0.0.5
func (s *Session) MarkLeaf() (*LabelEntry, error)
MarkLeaf builds the label entry that makes the current leaf durable, so a reopened session resumes on this branch rather than on whichever one the last line happens to be under. Append it through the store after Branch; the leaf stays where it is, and appending past the mark moves the resumed leaf with it. It returns an error when there is no leaf.
A mark records which branch is live, so a host that moves to another branch marks again; nothing under an abandoned mark follows it, and the abandoned mark is what a reopen honours.
func (*Session) Migrated ¶ added in v0.0.8
Migrated reports whether the session was read from a file of an earlier minor version and rewritten in memory, so its entries carry LegacyID, and returns the IDs of entries the migration could not rewrite: extension entries, whose members may name entries the reader cannot recognise. A migrated session with unresolved entries cannot be written as 0.5.
func (*Session) OpenRun ¶ added in v0.0.5
OpenRun returns the run on the path to leaf that has no end entry, or nil when every run is closed or there is none.
func (*Session) Path ¶
Path returns the entries from the first entry to id, the first entry first, or nil when id is not in the session. The path ends, above its first entry, at the universal root.
func (*Session) PendingCalls ¶ added in v0.0.5
PendingCalls returns the calls on the path to leaf that have no output, which is what a resume answers.
func (*Session) PendingQueued ¶ added in v0.0.6
func (s *Session) PendingQueued(leaf string) ([]*QueuedEntry, error)
PendingQueued returns the inputs queued on the path to leaf that have not been appended; see Queued.
func (*Session) Prefix ¶ added in v0.0.8
Prefix reports whether id is on the path to the session's base: an entry another session wrote, carried so the file stands alone.
func (*Session) Prepare ¶ added in v0.0.8
Prepare does everything Append does short of adding the entry: it fills the parent and the timestamp, sorts and checks the references, applies the parent rule, and computes the hashes, setting the ID on the entry. It reports the outcome Commit would have. A store uses it to know the entry's hashes before anything is written, so that nothing is visible in memory before the store's commit point; a Commit of the same entry afterwards recomputes the same values.
func (*Session) RaiseRefused ¶ added in v0.0.27
RaiseRefused reports whether the session may be written under format, as a writer raising its header to format would write it: nil when it may, and ErrNoRaise, wrapped, when format is 0.13 or later and the session holds a member that minor would give a meaning it was not written with: its header names a minor before 0.13 and holds redacted_from, or an entry holds a redacted member it was read with under a header naming such a minor, which RedactedOf reports as not redacted. That is one rule for a raise and for a new file: an append (Session.Append and Session.Prepare refuse with it), Fork, which refuses a fork at 0.13 or later whose prefix holds such an entry, and a store. Header.Raise sees the header alone, so a store that raises a session's header outside an append, as an exchange or a fork's create does, calls RaiseRefused on the session it holds first.
func (*Session) Repeated ¶ added in v0.0.8
Repeated returns the IDs Read met a second time in the file, each treated as the same entry as the first, in file order.
func (*Session) RequestContext ¶
RequestContext rebuilds the context of the request that produced the response entry id: the path to the entry with the response's own output items removed, as OutputEntries finds them. Only those item entries are removed; everything else on the path stays, so an entry another layer wrote between two output items of one response, which contributes nothing to context, leaves the rebuilt request and its hash alone.
func (*Session) RequestHashFor ¶ added in v0.0.28
func (s *Session) RequestHashFor(parent string, r *ResponseEntry) (string, error)
RequestHashFor computes the request_hash r would verify with were it appended under parent: the hash of the request the session rebuilds for it, the path to parent with r's own output items removed, as Session.Verify rebuilds and hashes it, through the same code, so a response that carries the value verifies. r's output items are the items on that path that name its ResponseID, which a writer appends before r. An empty parent is the session's leaf, where Session.Append and Session.Commit put an entry whose Parent is empty. A parent the session does not hold is ErrNoEntry. r is not changed, and its own RequestHash plays no part.
It is how a redactor gives a redacted response its request_hash (RFC 0001, Redaction): a redacted session hashes independently, so the value is the one its own request rebuilds to, never the original's, which no longer matches once anything on the path was redacted. Commit the entries the response's path holds first, in the session the response will be committed to. A redacted response gets a request_hash only when the entry it was made from carried one; one whose original recorded none gets none, since nothing vouches that the request was sent as recorded. Session.SetRequestHash applies that rule.
func (*Session) ResetLeaf ¶
func (s *Session) ResetLeaf()
ResetLeaf moves the leaf to the universal root, so the next append is a first entry.
func (*Session) Resolve ¶ added in v0.0.9
Resolve returns the ID of the entry id names: id itself when it is an entry's ID, else the ID of the entry a migration rewrote from id, whose LegacyID it is, in the session as read or in a 0.5 file that kept the member. Everything written about a session before its file was migrated names entries by their old IDs; Resolve is how a reader holding one finds the entry, since Entry, Path and the rest take the current ID alone.
func (*Session) Roots ¶
Roots returns the IDs of the first entries, those that hang from the universal root, in file order. A session with a base has one; one without may branch at the root.
func (*Session) SetRequestHash ¶ added in v0.0.28
func (s *Session) SetRequestHash(r *ResponseEntry) error
SetRequestHash sets r's RequestHash to Session.RequestHashFor under r's Parent, empty meaning the leaf, as Commit fills it, when r carries one, and leaves r without one when it carries none. A redactor calls it on each response it has rewritten and remapped, which still holds the original's request_hash or none, just before committing it: the original's is replaced, and a response whose original recorded none gets none (RFC 0001, Redaction). A response made from one whose request_hash did not verify in the original must get none, so a redaction never certifies a request the original could not: the redactor clears RequestHash on such a response before calling, since only it has the original. On an error r is not changed.
func (*Session) SummarizeBranch ¶
func (s *Session) SummarizeBranch(from string, summary openresponses.Item) (*BranchSummaryEntry, error)
SummarizeBranch builds the branch summary that carries context from the abandoned leaf from to the current leaf, where the new branch continues. Move the leaf with Branch first, then append the result through the store; its parent is set on append. When the new leaf is inside a run, that run is open on the new path and the writer closes it first: append Session.EndRun with ReasonInterrupted before the summary, so the path does not read as a run a crash cut off.
func (*Session) SupersededBy ¶ added in v0.0.5
SupersededBy returns the successor named by the last continued_in link in the session, or "".
func (*Session) Truncated ¶
func (s *Session) Truncated() *TruncatedLine
Truncated reports the final line of the file this session was read from when that line did not parse, or nil. A truncated line is what a crash mid-append leaves behind; the entries before it are intact.
func (*Session) UnresolvedOf ¶ added in v0.0.20
func (s *Session) UnresolvedOf(leaf string) ([]*ConfigEntry, error)
UnresolvedOf returns the config entries on the path to leaf that carry a keep with an of the path cannot resolve; see UnresolvedOf.
func (*Session) Verify ¶
Verify rebuilds the request for the response entry id and checks that it hashes to the entry's RequestHash. A response that recorded no hash returns ErrNoHash: nil means the request was checked. A redacted session is held to it as any other: its responses' hashes are its own, which a redactor sets with Session.SetRequestHash, so a mismatch on a redacted entry is one like any other (RFC 0001, Redaction).
func (*Session) VerifyLinks ¶ added in v0.0.20
VerifyLinks checks every subsession link the session wrote against the header of the session it names, read through resolve: the target's parent_session is this session's id and its spawned_by is the link's call_id, as RFC 0001 says of a subsession. A target resolve does not find, returned as (nil, nil), is a child that never started, which a link written at dispatch records by design, and passes; an error from resolve is reported for that link. A link on a fork's prefix is the origin's, to be checked there against the origin's id, since the child it names was spawned by the origin; it is passed over, as are links of other relations, which name no spawn, a cancel notice, and a link naming a callee in another system (its URL set), whose session was not derived from this one. It returns the first problem found, a *LinkError, and with a nil resolve checks nothing.
func (*Session) VerifyRecords ¶ added in v0.0.5
VerifyRecords checks the record entries on the path to leaf against the format's rules: every function call has a call ID and none repeats in the session, every decision and dispatch names its call by target, every run start's source and every run end agree with the segment, no dispatch or decision follows a reject or an answer on the same call, no answer, reject or dispatch follows an output and no reject a dispatch but a withdrawal, every item's from_link names a peer link before it, and, when the header names dispatch in records, no answer ends a call with no dispatch and every call that ran has a dispatch. A run written resume over a segment that takes nothing up, holding record entries at most, agrees with it, as Run.Empty says, in a file of any minor. The rules that rest on records apply to what the session wrote, the entries after its base: a fork's prefix is another session's record, kept to that session's promise. It returns the first problem found.
type Settings ¶
type Settings struct {
Model string `json:"model,omitempty"`
Instructions string `json:"instructions,omitempty"`
Reasoning openresponses.ReasoningConfig `json:"reasoning,omitzero"`
Text openresponses.TextConfig `json:"text,omitzero"`
// InstructionsParts are the parts the instructions are composed
// of, in order, when the path named them, each carrying its text.
// Instructions is always their texts joined with [PartSeparator],
// so a reader that does not care about the composition reads the
// string as before. It is empty for a path that set the
// instructions as one string.
InstructionsParts []InstructionPart `json:"instructions_parts,omitempty"`
// InstructionsOmitted are the parts left out of the instructions
// that are in force: the list the last config entry carrying one
// wrote, each keep in it resolved against the list before it,
// cleared by an empty list or a replace without one. A keep the
// path could not satisfy stays in the list as written: see
// [OmittedPart.Unresolved]. It
// reaches no request, so [Settings.Request] leaves it out and the
// request hash does not cover it; a compaction checkpoint carries
// it so the list survives the fold. A checkpoint member that does
// not decode as a list of parts, which a file from before 0.8 may
// hold, is kept as written and InstructionsOmitted is nil.
InstructionsOmitted []OmittedPart `json:"instructions_omitted,omitempty"`
// Omit is what a request leaves out of the context, in force as the
// path's config entries set it: see [Omit]. The checkpoint of a
// compaction carries it. A checkpoint member that does not decode as
// the object, which a file from before 0.11 may hold, is kept as
// written and Omit is zero.
Omit Omit `json:"omit,omitzero"`
Tools openresponses.Tools `json:"tools,omitempty"`
// Extra carries request members beyond the named ones, keyed by
// their wire name.
Extra map[string]json.RawMessage `json:"extra,omitempty"`
// contains filtered or unexported fields
}
Settings are the request settings in force at a point on a path: the result of replaying config entries. The JSON form is the full-config checkpoint a compaction entry carries.
func (Settings) Apply ¶
func (s Settings) Apply(c *ConfigEntry) Settings
Apply returns the settings after the delta c. The receiver is not modified.
func (Settings) ExtraValue ¶ added in v0.0.3
ExtraValue decodes the passthrough member key into v. It reports false, and leaves v alone, when the settings carry no such member.
func (Settings) InstructionsDelta ¶ added in v0.0.6
func (s Settings) InstructionsDelta(parts []InstructionPart) *ConfigEntry
InstructionsDelta returns the config delta that takes the instructions from these settings to parts: the whole ordered list, with the text of every part that is new or whose text or source changed, a keep for every run of unchanged parts in the order they are in force, and the hash alone of an unchanged part out of that order, so a change to one layer costs that layer and not the whole prompt, however many parts it has. A part in force that parts leaves out is removed by its absence. parts names each ID once.
A part that is not in force, or is in force with other resolved text, is named by its hash alone when the path has given its ID that text and source and the part has since left force, as format 0.11 lets a writer: an agent handed the session back after another replaced its parts costs the parts that changed, and not its whole prompt. A part in force with its text and another source, and one in force the path could not resolve, carry their text, since a hash resolves against the part in force first, and so does a part the path never had under its ID. A replace and a compaction's checkpoint start the path's parts afresh, so a part that left force before one carries its text again. A delta written this way is read by a reader of 0.11, which resolves a hash against the parts that have left force; the library writes 0.11 headers, so a reader of 0.10 that cannot has refused the file.
It returns nil when parts are exactly the ones in force, so a harness that re-renders its layers every turn writes nothing when nothing moved.
func (Settings) OmitDelta ¶ added in v0.0.20
OmitDelta returns the omit member that takes the omit in force in these settings to want, for ConfigEntry.Omit, and whether one does. It is nil, and true, when want is what is in force, so a recorder that states its rule at every model switch writes it once; the rule, the entries and the outputs not yet in force, the outputs' text when it changed, and the images rule when it changed, when want only adds to what is, a rule other than the one in force included, which a delta replaces; and an empty, non-nil object, which clears, when want is empty and something is in force. A want that drops the rule, an entry, an output or the images rule in force and keeps something is no single member: a delta only adds, and clears everything with {}, so it returns nil and false, and the writer clears in one entry and writes what it keeps in the next. So does a want whose rule is not one the format defines, which no delta sets.
A config entry with Replace set discards the omit in force with the rest of the settings, so the member it carries is want itself, not this delta: a replace that wrote nothing, because the rule was in force, would clear it.
func (Settings) OmittedDelta ¶ added in v0.0.12
func (s Settings) OmittedDelta(omitted []OmittedPart) []OmittedPart
OmittedDelta returns the instructions_omitted member that takes the omitted parts in force in these settings to omitted, for ConfigEntry.InstructionsOmitted: nil when omitted is the list in force, so a writer that renders its omissions every turn writes nothing when nothing moved; an empty, non-nil list, written as [], when omitted is empty and a list is in force; and otherwise the shortest of three, as encoded: omitted whole; omitted with every run of parts in force, unchanged and in the order they are in force, named by a keep, so a part moving across a budget costs that part and not the whole list; and omitted with every run of parts that an earlier entry on the path put in force, unchanged and in that list's order, named by a keep carrying of, so a hand-back to an agent whose list another agent replaced costs one element and not the list. Each of the sixteen lists put in force most recently before the one in force is tried, newest first, and one is used only when it is shorter than the best so far, so a list in force that the keeps already cover is never named by of. A part that changed, is new or is out of the order of the list a delta counts over is written whole. omitted names each ID once; when it does not, omitted is returned whole, and a list a delta would count over that names an ID twice is not counted over.
The lists an of can name are those the settings recorded as they were replayed, so settings from Session.Context or BuildContext have them, back to the last replace or compaction; settings built by hand have none, and the result is the first two. An of is the ID of the config entry, so a recorder writes the delta for the entry it is about to append and nothing else.
A delta with Replace set discards the lists in force and earlier ones, so its keeps would resolve against nothing: such a delta carries omitted itself.
func (Settings) Request ¶
func (s Settings) Request(items openresponses.Items) (openresponses.Request, error)
Request is Settings.RequestJSON as an openresponses.Request, the form a harness sends. An Extra member the request type declares, such as temperature, fills its field, since openresponses never takes a declared member from Request.Extra, and every other member rides in Request.Extra, number literals as written. The struct is lossier than the JSON: a declared member recorded empty is dropped on encode, and one whose value its field cannot hold is an error, so the request hash and a continuation are taken from RequestJSON, never from this.
func (Settings) RequestJSON ¶ added in v0.0.31
func (s Settings) RequestJSON(items openresponses.Items) ([]byte, error)
RequestJSON builds the canonical request for these settings over items as the JSON object the request hash is taken over: store false and no previous_response_id, as the context algorithm requires, the named settings, and every Extra member exactly as recorded, a member the settings set by name winning over an Extra member of the same name. It is what Session.Verify hashes, and it holds what Settings.Request cannot: a declared member recorded empty, such as "stream":false or "include":[], or with a value its typed field cannot hold, such as "metadata":{"n":1}.
func (*Settings) UnmarshalJSON ¶ added in v0.0.11
UnmarshalJSON decodes a checkpoint, taking instructions_omitted only when the member is spelled exactly and holds a list of parts that encodes back to what the line holds, extra members aside, as a member promoted into a typed field is taken. Anything else, such as a part whose id is spelled "ID", is left for the rewrite to keep as written.
type Span ¶ added in v0.0.27
type Span struct {
From, To int
}
Span is one element of an entry's redacted member: the entries of the original from From to To, inclusive, by position in the original's file order, prefix included and counting from 1. A single position has From == To and is written as the number n; a range has From < To and is written as [from, to].
func RedactedOf ¶ added in v0.0.27
RedactedOf reports whether e is a redacted entry, one that carries the redacted member in the form the format defines, and returns its spans: the positions, in the original's file order, of the entries e was made from. An entry the redaction added, made from none, is redacted with no spans. A member in another form, or one read from a file whose header names a minor before 0.13, where the member is not defined, is one this package does not know: it stays in Unknown (or, on an extension entry, in its raw line) as written, and RedactedOf reports false. Read, RawEntry.Decode and Session.Extend apply the header's minor; UnmarshalEntry, given a line alone, decodes it as this package writes it, and GateEntry applies the minor to it. The spans are the entry's own, so a caller must not modify them.
func (Span) MarshalJSON ¶ added in v0.0.27
MarshalJSON writes a single position as n and a range as [from, to].
type Store ¶
type Store interface {
// Create starts a new session from h, filling empty header fields.
// A header whose Base is set makes a fork, as [Fork] does: the
// session opens with the path to the base, taken from the session
// ParentSession names when that session holds it and otherwise from
// any session the store holds it in. Create refuses a base the store
// does not hold with ErrNoEntry, and a base that is a leaf label or a
// media form other than the origin's, rather than letting the first
// append fail.
Create(ctx context.Context, h Header) (*Session, error)
// Open loads the session with the given ID. A store that guards
// sessions against a second writing process takes the session's
// hold here and keeps it until the store lets it go, at its Release
// or Close: one hold per session per store, which a Release frees
// whoever opened it. A process that reads sessions it does not
// write, such as a search across a history beside the harness
// writing it, reads them through [Reader] or through a second store
// opened read-only, never by Open and Release on the writing store,
// which would keep each session from other processes or free one
// its own writer is using.
Open(ctx context.Context, id string) (*Session, error)
// Append adds e to the session and returns its ID.
Append(ctx context.Context, sessionID string, e Entry) (string, error)
// List enumerates sessions matching f, newest first.
List(ctx context.Context, f ListFilter) iter.Seq2[Summary, error]
// Delete removes the session.
Delete(ctx context.Context, id string) error
}
Store persists sessions. Append is the only write to a session's content; the entry is added to the in-memory tree of the open session and made durable according to the store's policy. Sessions returned by Create and Open are shared with the store, so leaf moves through Session.Branch and Session.ResetLeaf are seen by later appends.
A store that guards a session against a second writing process reports ErrSessionLocked from the calls that need the hold, and a store opened read-only reports ErrReadOnly from the calls that write.
type Summary ¶
type Summary struct {
Header Header
// Name is the session's display name, from the last info entry
// that set one, when the store provides it: see
// ListFilter.WithNames.
Name string
// SupersededBy names the successor the session was continued in,
// from its last continued_in link, when the store provides it: a
// store that indexes links fills it always, a file store when
// ListFilter.WithNames or Current asks it to scan.
SupersededBy string
// Path is where the session lives, for file-backed stores.
Path string
// Size is the stored size in bytes, when known.
Size int64
// Modified is when the session last changed, when known.
Modified time.Time
}
Summary describes a stored session without loading it.
type Trigger ¶ added in v0.0.6
type Trigger struct {
Kind string `json:"kind,omitempty"`
Ref string `json:"ref,omitempty"`
Source string `json:"source,omitempty"`
// Unknown holds the members of the trigger the format does not
// define, the harness's own facts about the arrival such as when a
// scheduled firing was due and which attempt it is, encoded inline
// beside kind, ref and source. It is nil when there are none.
Unknown map[string]json.RawMessage `json:"-"`
}
Trigger says how an input arrived, in the harness's own terms: Kind is the sort of thing it came from (a person, a channel, a schedule, another agent), Ref names the thing itself (a message ID, a cron name) and Source names the layer that took it. A reader treats all three as opaque; what the format fixes is where they are written, so two people steering one run are two triggers and not one.
func (*Trigger) Clone ¶ added in v0.0.11
Clone returns a copy of the trigger that shares no map with it, or nil for nil.
func (*Trigger) Equal ¶ added in v0.0.11
Equal reports whether two triggers are the same: kind, ref, source and every member the format does not define, compared in canonical form, so the order and spacing a line gave them do not count. Two nil triggers are equal. A Trigger holds a map, so == does not compile on it.
func (Trigger) MarshalJSON ¶ added in v0.0.11
MarshalJSON emits kind, ref, source and the unknown members as one object.
func (*Trigger) SetMember ¶ added in v0.0.11
SetMember sets a member of the trigger the format does not define, such as when a scheduled firing was due or which attempt it is.
func (*Trigger) UnmarshalJSON ¶ added in v0.0.11
UnmarshalJSON takes kind, ref and source, spelled exactly, and keeps every other member in Unknown.
type TruncatedLine ¶
type TruncatedLine struct {
// Line is the 1-based line number in the file.
Line int
// Data is the line as read, without its terminator.
Data []byte
// Err is the decode error.
Err error
}
TruncatedLine describes a final line that did not parse.
func (*TruncatedLine) Unwrap ¶
func (t *TruncatedLine) Unwrap() error
Unwrap returns the decode error.
type UnknownEntry ¶
type UnknownEntry struct {
EntryBase
Type string
Raw json.RawMessage
}
UnknownEntry is an entry whose type this package does not define, typically a namespaced extension. Raw holds the original line and is re-emitted verbatim. It is not in context.
func (*UnknownEntry) EntryType ¶
func (e *UnknownEntry) EntryType() string
EntryType returns the wire type.
func (*UnknownEntry) MarshalJSON ¶
func (e *UnknownEntry) MarshalJSON() ([]byte, error)
MarshalJSON emits the original bytes.
func (*UnknownEntry) UnmarshalJSON ¶
func (e *UnknownEntry) UnmarshalJSON(data []byte) error
UnmarshalJSON records the envelope and the raw bytes.
type UnmetPart ¶ added in v0.0.31
type UnmetPart struct {
// Entry is the ID, in the original, of the entry holding the part.
Entry string
// At is the part's member in that entry, an RFC 6901 JSON Pointer
// such as /instructions_parts/2/hash, or /instructions_parts/0/keep
// for a keep.
At string
// ID is the part's id as the original wrote it, empty for a keep
// and for a part with none.
ID string
}
UnmetPart is an instruction part a Rewriter wrote that the path it is on cannot rebuild in the redacted session: a part named by a hash whose text no part in force on its path holds, as the Rewriter replays the entries it was given and not those it was told were left out, or a keep naming parts no entry on its path put in force. The first keeps the original's hash, which a redacted session MUST NOT keep, as it neither verifies there nor hides the text, and a hash rewritten from a text met elsewhere resolves nowhere on the path; the second names parts the redacted path lacks (RFC 0001, Every redacted entry). A redactor writes such a part as text of its own, such as fixed placeholder text, and gives the responses after it no request_hash or a recomputed one, or it does not redact the entry.
The part's hash and text are not carried. Entry and ID are values of the original, an entry's hash and a part's id as written there, for the redactor's own use: a hash of the original can confirm a guessed text, and an id may hold what is redacted, so neither is to be logged or published.
type VCS ¶
type VCS struct {
System string `json:"system"`
Revision string `json:"revision,omitempty"`
Dirty bool `json:"dirty,omitempty"`
// Unknown holds the members of vcs the format does not define,
// encoded inline beside system, revision and dirty. It is nil when
// there are none.
Unknown map[string]json.RawMessage `json:"-"`
}
VCS is the version-control state of the working directory. Revision and Dirty cannot tell two dirty trees on one revision apart; a harness that needs to, as one that restores files to a checkpoint does, records the tree's identity in a namespaced member, such as "cline:tree", with VCS.SetMember. A change to it is not a substitution, since only workspace is compared. Replacing an entry's VCS whole drops such members; a caller changing a field edits the VCS it has, or a copy of it, which keeps them.
func (VCS) MarshalJSON ¶ added in v0.0.19
MarshalJSON emits the defined members and the unknown ones as one object.
func (*VCS) SetMember ¶ added in v0.0.19
SetMember sets a member of vcs the format does not define, which should be namespaced, such as "cline:tree".
func (*VCS) UnmarshalJSON ¶ added in v0.0.19
UnmarshalJSON takes system, revision and dirty, spelled exactly, and keeps every other member in Unknown.
type Withdrawal ¶ added in v0.0.31
type Withdrawal struct {
Dispatch *DispatchEntry
Reject *DecisionEntry
}
Withdrawal is one hand-off withdrawn before the tool ran: the dispatch, and the reject carrying withdrawn that directly follows it among the call's entries (RFC 0001, decision).
type Workspace ¶ added in v0.0.5
type Workspace struct {
Kind string `json:"kind,omitempty"`
Ref string `json:"ref,omitempty"`
// Host names the machine the file system is on, as the harness names
// it: a hostname, a node name. A writer sets it whenever another
// machine could hold a file system with the same Kind and Ref, every
// local workspace among them.
Host string `json:"host,omitempty"`
// Instance names the file system among those Kind, Ref and Host
// describe: a container's or a sandbox's ID, since a restart from the
// same image is another file system with the same digest. A writer
// sets it whenever a restart gives a new file system.
Instance string `json:"instance,omitempty"`
// Unknown holds the members of workspace the format does not define,
// encoded inline beside the others, and a host or an instance member
// that is not a non-empty string, which a file from before 0.14 was
// free to write, as written. It is nil when there are none.
Unknown map[string]json.RawMessage `json:"-"`
}
Workspace says which file system an env entry's cwd is a path in. Ref is one string the harness can resolve to it, an image digest or an endpoint, and is empty for a local workspace. A container's Ref should be a digest rather than a tag, because a tag moves. Host and Instance, which the format defines since 0.14, name the machine the file system is on and the file system among those Kind, Ref and Host describe, such as a container's ID. Anything else that tells one file system from another goes in Unknown: inside workspace and not beside it, since the format's substitution rule compares workspace alone: see SameWorkspace.
func (Workspace) MarshalJSON ¶ added in v0.0.10
MarshalJSON emits kind, ref, host, instance and the unknown members as one object.
func (*Workspace) SetMember ¶ added in v0.0.10
SetMember sets a member of the workspace the format does not define, such as the zone a node is in. Host and instance are fields; for either, a non-empty string sets the field, as a reader takes one, and any other value is refused.
func (*Workspace) UnmarshalJSON ¶ added in v0.0.10
UnmarshalJSON takes kind and ref, spelled exactly, and host and instance, spelled exactly, when each holds a non-empty string, and keeps every other member in Unknown as written.
type Writer ¶ added in v0.0.23
Writer is implemented by a store that reports what an append did, as RFC 0002 asks of a store: the outcome (continued, branched, held, a leaf label's move or not), whether the entry was durable when the call returned, and the references it could not resolve. Write is Append with that Result; a store that implements it keeps the two the same append. A store over a network (RFC 0003's remote client) returns what the server answered.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package atif holds Go types for the Agent Trajectory Interchange Format (ATIF) v1.8, Harbor's interchange format for agent trajectories, as defined by its RFC and the Pydantic models in harbor.models.trajectories.
|
Package atif holds Go types for the Agent Trajectory Interchange Format (ATIF) v1.8, Harbor's interchange format for agent trajectories, as defined by its RFC and the Pydantic models in harbor.models.trajectories. |
|
Package cas is the content-addressed session store RFC 0002 describes, laid out on a filesystem the way git lays out a repository:
|
Package cas is the content-addressed session store RFC 0002 describes, laid out on a filesystem the way git lays out a repository: |
|
cmd
|
|
|
agentsession
command
Command agentsession inspects, verifies, exports and lists Agent Session Format files, and the sessions of a cas store, from a shell, repairs a cas session whose log is damaged, and migrates a cas store from before per-session logs.
|
Command agentsession inspects, verifies, exports and lists Agent Session Format files, and the sessions of a cas store, from a shell, repairs a cas session whose log is damaged, and migrates a cas store from before per-session logs. |
|
Package export turns session trees into linear trajectories and ATIF documents.
|
Package export turns session trees into linear trajectories and ATIF documents. |
|
internal
|
|
|
follow
Package follow is the loop the file and database stores share to implement agentsession.Follower: a store says how to read a session up to a cursor and what has been written beyond one, and Run turns that into the iterator, keeping the follower's own session, yielding the changes in order and waiting between them.
|
Package follow is the loop the file and database stores share to implement agentsession.Follower: a store says how to read a session up to a cursor and what has been written beyond one, and Run turns that into the iterator, keeping the follower's own session, yielding the changes in order and waiting between them. |
|
followtest
Package followtest is the helper the Follow tests of every store share: a follow ranged over in a goroutine, so a case can wait for the next change with a timeout and check that nothing else comes.
|
Package followtest is the helper the Follow tests of every store share: a follow ranged over in a goroutine, so a case can wait for the next change with a timeout and check that nothing else comes. |
|
ijson
Package ijson checks that a JSON document is I-JSON (RFC 7493) by the test the session format states: every number is finite once rounded to binary64, a number whose exact value is a whole number is exactly representable in binary64 or is the exact value of the canonical rendering of the nearest double, no object repeats a member name, and no string holds a lone surrogate.
|
Package ijson checks that a JSON document is I-JSON (RFC 7493) by the test the session format states: every number is finite once rounded to binary64, a number whose exact value is a whole number is exactly representable in binary64 or is the exact value of the canonical rendering of the nearest double, no object repeats a member name, and no string holds a lone surrogate. |
|
jcs
Package jcs implements the JSON Canonicalization Scheme (RFC 8785): members sorted by UTF-16 code units, no insignificant whitespace, strings with the minimal escapes the scheme prescribes and numbers in the ECMAScript Number.prototype.toString form.
|
Package jcs implements the JSON Canonicalization Scheme (RFC 8785): members sorted by UTF-16 code units, no insignificant whitespace, strings with the minimal escapes the scheme prescribes and numbers in the ECMAScript Number.prototype.toString form. |
|
jsonx
Package jsonx holds the encoding/json helpers the agentsession packages share: escape-free marshalling, member joining for objects with unknown members, and struct key discovery.
|
Package jsonx holds the encoding/json helpers the agentsession packages share: escape-free marshalling, member joining for objects with unknown members, and struct key discovery. |
|
origin
Package origin picks a fork's origin from the sessions a store found holding its base, asking the caller's read check (agentsession's WithReadable) only once the store has let go of its own locks: the check is a program's authorizer, which may read the store.
|
Package origin picks a fork's origin from the sessions a store found holding its base, asking the caller's read check (agentsession's WithReadable) only once the store has let go of its own locks: the check is a program's authorizer, which may read the store. |
|
procs
Package procs answers one question for the stores' session locks: does a process with this ID still exist on this host.
|
Package procs answers one question for the stores' session locks: does a process with this ID still exist on this host. |
|
wake
Package wake carries the two pieces of waiting a follower shares across stores: a per-session broadcast a writer in the same process rings, and the interval a follower polls at when nothing rings.
|
Package wake carries the two pieces of waiting a follower shares across stores: a per-session broadcast a writer in the same process rings, and the interval a follower polls at when nothing rings. |
|
Package jsonl is the default session store: one append-only JSONL file per session under a root directory, laid out as
|
Package jsonl is the default session store: one append-only JSONL file per session under a root directory, laid out as |
|
otel
module
|
|
|
Package remote is RFC 0003, the Agent Session Store Protocol: a store reached over HTTP, for a reader, a follower or a writer in another process or on another machine.
|
Package remote is RFC 0003, the Agent Session Store Protocol: a store reached over HTTP, for a reader, a follower or a writer in another process or on another machine. |
|
sqlite
module
|
|
|
Package storetest is the conformance suite for agentsession.Store implementations.
|
Package storetest is the conformance suite for agentsession.Store implementations. |