Documentation
¶
Overview ¶
Package decisions extracts the decisions made in an agent session: the choices between plausible alternatives that land in the repository.
It holds what every host shares: the decision schema, the cleaned transcript a model reads (Render), the strict reply schema and parser, and the citation validator that checks a reply against the transcript (Extractor). A host supplies its own system prompt to NewExtractor, or uses the published one (NewPublicExtractor), and its own Model. See ARCHITECTURE.md, "decisions".
Index ¶
- Constants
- Variables
- func ContentHash(t *Transcript) string
- func EstimateTokens(system, user string) int64
- func ExtensionCutoff(t *Transcript, prefixHash string) (int, bool)
- func ExtractSchema() map[string]any
- func HumanText(input session.UserInput) string
- func MaxRequestTokens(req ModelRequest) int64
- func PrefixHash(t *Transcript) string
- func PublicPrompt() string
- func ReportOnCall(use session.ToolUse) bool
- func ValidateCitations(decisions []Decision, transcript *Transcript, cutoff int) ([]Decision, []Dropped)
- type BilledError
- type Category
- type CitedPaths
- type Decision
- type Dropped
- type Event
- type EventKind
- type ExtractOptions
- type Extraction
- type Extractor
- func (e *Extractor) EstimateTokens(transcript *Transcript, opts ExtractOptions) int64
- func (e *Extractor) Extract(ctx context.Context, model Model, transcript *Transcript, opts ExtractOptions) (Extraction, error)
- func (e *Extractor) IsRequest(req ModelRequest) bool
- func (e *Extractor) MaxTokens(transcript *Transcript, opts ExtractOptions) int64
- func (e *Extractor) PromptVersion() string
- func (e *Extractor) Request(user string, maxOutputTokens int64) ModelRequest
- func (e *Extractor) SystemPrompt() string
- type Model
- type ModelRequest
- type ModelResponse
- type Option
- type PlanOutcome
- type Question
- type Redactor
- type Row
- type SessionDecisions
- type Transcript
- type Tree
- func (r *Tree) EditedPath(touch session.FileTouch) (string, bool)
- func (r *Tree) NotificationReport(input session.UserInput) (report, spawnToolUseID string)
- func (r *Tree) Result(toolUseID string) *session.ToolResult
- func (r *Tree) ScrubPaths(text string) string
- func (r *Tree) SortRows(rows []Row)
- func (r *Tree) SpawnedSession(toolUseID string) (string, bool)
- func (r *Tree) ToolEvent(use session.ToolUse) (Event, bool)
- type Trigger
- type Turn
- type Usage
- type Who
Constants ¶
const CustomVersionPrefix = PublicPromptName + "+custom"
CustomVersionPrefix starts the PromptVersion of an extraction under a prompt other than the published one; "-" and the prompt's hash follow it.
const ExtractSchemaName = "session_decisions"
ExtractSchemaName names ExtractSchema in ModelRequest.SchemaName.
const FramingTokens = 1024
FramingTokens allows for what a request adds around its text: message and role framing, and the tool definition Anthropic wraps the schema in.
const PublicPromptName = "decisions-public-v1"
PublicPromptName names the published extraction prompt.
Variables ¶
var ErrTranscriptTooLarge = errors.New("transcript exceeds the input token cap")
ErrTranscriptTooLarge reports a transcript over ExtractOptions.MaxInputTokens.
Functions ¶
func ContentHash ¶
func ContentHash(t *Transcript) string
ContentHash is the SHA-256, in hex, of t's Text: the content a cached extraction of t is keyed on.
func EstimateTokens ¶
EstimateTokens is the prompt size Extract compares with ExtractOptions.MaxInputTokens: the bytes of the system prompt and the transcript divided by four, rounded up. It ignores the schema and message framing, and overestimates for long words; it only needs to be cheap and deterministic.
func ExtensionCutoff ¶
func ExtensionCutoff(t *Transcript, prefixHash string) (int, bool)
ExtensionCutoff finds the prefix of t's turns, short of all of them, whose text hashes to prefixHash, an earlier extraction's PrefixHash. It returns the ordinal of that prefix's last turn, the cutoff to extend that extraction with, and whether one matched.
func ExtractSchema ¶
ExtractSchema returns the JSON Schema of the model's reply, {"decisions": [...], "superseded_existing": [...]}. It is valid under OpenAI strict structured outputs, so optional values have an empty form: "" for no rationale, rejected alternative or evidence, decided_turn 0 for none and superseded_existing [] outside an extension. Each "properties" value is an orderedObject, so the fields marshal in the order the model should write them.
func HumanText ¶
HumanText is the prompt a person typed, cleaned as the transcript shows it, or empty for inputs nobody typed.
func MaxRequestTokens ¶
func MaxRequestTokens(req ModelRequest) int64
MaxRequestTokens is the same upper bound for any request: one token per byte of its system prompt, user message and marshalled schema, plus FramingTokens.
func PrefixHash ¶
func PrefixHash(t *Transcript) string
PrefixHash is the ContentHash of t without its last turn, which a later extraction of the grown session extends from (ExtensionCutoff); "" when t has at most one turn.
func PublicPrompt ¶ added in v0.2.0
func PublicPrompt() string
PublicPrompt returns the text of the published extraction prompt: the built-in text a custom prompt replaces.
func ReportOnCall ¶
ReportOnCall reports whether a spawning call's result is its subagent's final report (session.SubagentReport).
func ValidateCitations ¶
func ValidateCitations(decisions []Decision, transcript *Transcript, cutoff int) ([]Decision, []Dropped)
ValidateCitations checks parsed decisions against the transcript they were extracted from. It removes turns that are not transcript ordinals and files not edited in a remaining cited turn. A decision is dropped whole when no turn remains or its own shape is invalid. An unknown trigger, a decided turn that is not a remaining cited turn, and evidence not quoted from one are removed on their own. With a positive cutoff, a decision none of whose remaining turns is after it is dropped: an extension's existing decisions already hold it. Order is kept, and each removal is reported.
Types ¶
type BilledError ¶
BilledError is a call the provider answered, and so billed, whose reply cannot be used: truncated, refused, failed or malformed. Model and Usage are what the provider reported for it.
func (*BilledError) Error ¶
func (e *BilledError) Error() string
func (*BilledError) Unwrap ¶
func (e *BilledError) Unwrap() error
type Category ¶
type Category string
Category is the kind of choice a decision makes.
const ( CategoryArchitecture Category = "architecture" CategoryDataModel Category = "data_model" CategoryInterface Category = "interface" CategoryBehavior Category = "behavior" CategoryImplementation Category = "implementation" CategoryDependency Category = "dependency" CategoryTesting Category = "testing" CategoryScope Category = "scope" // CategoryProcess is CI, build and dev tooling changed in the repository, // never issue tracking, orchestration or PR mechanics. CategoryProcess Category = "process" )
type CitedPaths ¶
type CitedPaths struct {
// contains filtered or unexported fields
}
CitedPaths matches the files a decision cites against a set of repository paths, such as a diff's. A decision may cite a file relative to the session's working directory rather than to the repository root.
func NewCitedPaths ¶
func NewCitedPaths(paths []string) CitedPaths
NewCitedPaths indexes paths; a path given twice counts once.
type Decision ¶
type Decision struct {
Title string `json:"title,omitempty"`
Summary string `json:"summary"`
Category Category `json:"category"`
Who Who `json:"who"`
Trigger Trigger `json:"trigger,omitempty"`
Rationale string `json:"rationale,omitempty"`
RejectedAlternative string `json:"rejected_alternative,omitempty"`
Superseded bool `json:"superseded"`
Turns []int `json:"turns"`
DecidedTurn int `json:"decided_turn,omitempty"`
Evidence string `json:"evidence,omitempty"`
EvidenceTurn int `json:"evidence_turn,omitempty"`
Files []string `json:"files"`
// ID is the decision's ID, given by the host when it first stores the
// extraction holding it (Identified); empty in a model reply.
ID string `json:"decision_id,omitempty"`
}
Decision is one decision made in a session. Turns are the root session's UserInput ordinals; files are repository-relative paths edited in one of those turns. DecidedTurn and EvidenceTurn are cited turns, zero for none; Evidence is a verbatim quote from EvidenceTurn. Trigger, Rationale, DecidedTurn and Evidence are empty in extractions from earlier prompt versions; labels carry no evidence.
func Identified ¶
Identified returns decisions with a random ID given to each one without one. The model never supplies an ID and none is derived, so a host calls it once, when it first stores an extraction, and keeps the IDs from then on.
func ParseReply ¶
ParseReply decodes a model reply to ExtractSchema strictly: invalid JSON, unknown or missing fields and trailing data are errors. It normalizes each decision: text trimmed and NUL-free, turns sorted and deduplicated, files cleaned and deduplicated, evidence whitespace collapsed. Who and category are not checked here, so a bad value drops one decision in ValidateCitations, not the reply. It also returns the superseded_existing handles, trimmed; a reply without them supersedes nothing.
type Dropped ¶
type Dropped struct {
// Decision is the decision's index in the model's reply.
Decision int `json:"decision"`
Summary string `json:"summary"`
// Field names a removed field (trigger, decided_turn or evidence); Turn
// then holds a removed decided turn. Without Field, Turn or File is the
// removed citation. All three are empty when the whole decision was
// dropped. Turn is a pointer so a removed turn 0 is kept.
Field string `json:"field,omitempty"`
Turn *int `json:"turn,omitempty"`
File string `json:"file,omitempty"`
Reason string `json:"reason"`
}
Dropped is a citation, a field, or a whole decision, removed by citation validation.
type Event ¶
type Event struct {
Kind EventKind
// Text is the agent text, plan or subagent report.
Text string
// Tool is the denied tool's name.
Tool string
Questions []Question
// Discuss reports that the human chose to talk the questions over
// instead of answering them.
Discuss bool
Outcome PlanOutcome
// Feedback is what the human said when rejecting a plan, a question or a
// tool call.
Feedback string
}
Event is one kept item in a turn, in session order.
type ExtractOptions ¶
type ExtractOptions struct {
// MaxInputTokens caps the estimated prompt size; a larger transcript
// returns ErrTranscriptTooLarge without calling the model. Zero means no cap.
MaxInputTokens int64
// MaxOutputTokens is passed to ModelRequest.MaxOutputTokens; zero means
// the provider client's default.
MaxOutputTokens int64
// Existing and Cutoff extend an earlier extraction of the same session
// instead of starting over, so its decisions and their IDs stay as they
// were. Existing are its decisions, shown to the model by handle (d1, d2,
// ...) and returned first; Cutoff is the last turn whose decisions they
// cover, 0 for none. Either one set extends. A cutoff is usually the turn
// before the earlier extraction's last turn (ExtensionCutoff), since an
// open last turn keeps growing.
Existing []Decision
Cutoff int
}
ExtractOptions configures Extract.
type Extraction ¶
type Extraction struct {
SessionDecisions
// PromptVersion is the Extractor's PromptVersion.
PromptVersion string `json:"prompt_version"`
Model string `json:"model"`
Usage Usage `json:"usage"`
Dropped []Dropped `json:"dropped,omitempty"`
}
Extraction is the result for one session.
type Extractor ¶
type Extractor struct {
// contains filtered or unexported fields
}
Extractor asks a model for the decisions in a transcript with one system prompt, and validates the reply. The host supplies the prompt, so a private hosted prompt and the published one (NewPublicExtractor) share the schema, parser and validator.
func NewExtractor ¶
NewExtractor returns an Extractor that sends systemPrompt.
func NewExtractorWith ¶ added in v0.2.0
NewExtractorWith returns an Extractor that sends system. The published prompt's text, or an empty one, gives exactly NewPublicExtractor's Extractor. Any other text gives NewExtractor's, with a PromptVersion of CustomVersionPrefix, "-" and NewExtractor's hash, so an extraction under a custom prompt is never taken for one under the published prompt.
func NewPublicExtractor ¶
func NewPublicExtractor() *Extractor
NewPublicExtractor returns an Extractor that sends the published prompt. Its PromptVersion is PublicPromptName and the hash NewExtractor's would be, so an extraction under it is never taken for one under another host's prompt, even one with the same text.
func (*Extractor) EstimateTokens ¶
func (e *Extractor) EstimateTokens(transcript *Transcript, opts ExtractOptions) int64
EstimateTokens is EstimateTokens of the request Extract would send for transcript and opts, the figure ExtractOptions.MaxInputTokens is compared with.
func (*Extractor) Extract ¶
func (e *Extractor) Extract(ctx context.Context, model Model, transcript *Transcript, opts ExtractOptions) (Extraction, error)
Extract asks model for the decisions in transcript and validates their citations against it (ValidateCitations). A transcript with no turns has no new decisions and does not call the model. An extension returns opts.Existing first, unchanged but for Superseded, then the new decisions, which have no IDs; a new decision none of whose turns is after opts.Cutoff is dropped.
func (*Extractor) IsRequest ¶
func (e *Extractor) IsRequest(req ModelRequest) bool
IsRequest reports whether req carries this Extractor's system prompt and the schema name, as Request builds it. A Model that sends only the transcript and lets the far side rebuild the request checks this first.
func (*Extractor) MaxTokens ¶
func (e *Extractor) MaxTokens(transcript *Transcript, opts ExtractOptions) int64
MaxTokens is an upper bound on the prompt tokens of the request Extract would send for transcript and opts, for reserving its cost before the call. Byte-level BPE tokenizers never produce more tokens than the text has bytes, so it counts one token per byte of the system prompt, the user message and the marshalled schema, plus FramingTokens. It is about four times EstimateTokens for English text, and holds for text with fewer bytes per token, such as CJK.
func (*Extractor) PromptVersion ¶
PromptVersion identifies the prompt and schema: the first 12 hex characters of the SHA-256 of the system prompt followed by the marshalled ExtractSchema. It changes whenever either changes, so cached extractions can be keyed on it.
func (*Extractor) Request ¶
func (e *Extractor) Request(user string, maxOutputTokens int64) ModelRequest
Request is the request Extract sends for user, Extract's user message (userMessage): a rendered transcript's text, extended or not.
func (*Extractor) SystemPrompt ¶
SystemPrompt is the system prompt the Extractor sends.
type Model ¶
type Model interface {
Complete(ctx context.Context, req ModelRequest) (ModelResponse, error)
}
Model makes one structured-output call. Provider clients implement it.
type ModelRequest ¶
type ModelRequest struct {
System string
User string
// SchemaName names Schema for providers that require one.
SchemaName string
// Schema is a JSON Schema object that satisfies OpenAI's strict structured
// outputs: every property required, additionalProperties false.
Schema map[string]any
// MaxOutputTokens caps the reply. Zero means the provider client's default.
MaxOutputTokens int64
}
ModelRequest is one call: a system prompt, one user message and the JSON Schema the reply must match.
type ModelResponse ¶
type ModelResponse struct {
// JSON is the reply object, expected to match the request's Schema.
JSON []byte
// Model is the model the provider reports having used.
Model string
Usage Usage
}
ModelResponse is the reply to a ModelRequest.
type Option ¶
type Option func(*settings)
Option configures Render.
func WithSessionFiles ¶
func WithSessionFiles(files ...sessioncorrelate.FileEvidence) Option
WithSessionFiles supplies the session's file evidence as sessioncorrelate.Correlate made it: repo-relative files for the session's repository, possibly built with evidence the graph does not hold, such as Entire's files_touched. They anchor relativization, and an edit counts only when Correlate also reports it as an edit, so files in another repository and paths Correlate could not prove stay out. A path a working directory cut short takes the spelling of the one stored edit it names (storedEdit).
func WithVendor ¶
WithVendor selects the vendor's attribute spellings for working directories, as for sessioncorrelate.Correlate.
type PlanOutcome ¶
type PlanOutcome string
PlanOutcome is what the human did with a proposed plan.
const ( PlanApproved PlanOutcome = "approved" PlanRejected PlanOutcome = "rejected" PlanUnanswered PlanOutcome = "unanswered" )
type Question ¶
type Question struct {
Text string
Options []string
// Answer is empty when no answer was recorded. For Cursor the answer, if
// any, is the next human prompt.
Answer string
// Picked reports that Answer is one (or, for multi-select, only) of the
// offered options rather than free text.
Picked bool
// Note is what the human added to their choice.
Note string
}
Question is one question the agent asked the human.
type Row ¶
type Row struct {
Message *session.AssistantMessage
ToolUse *session.ToolUse
Index int
}
Row is one assistant message or tool call to put in session order. Exactly one of Message and ToolUse is set. Index is the row's position in the graph's AssistantMessages or ToolUses; it breaks ties.
type SessionDecisions ¶
type SessionDecisions struct {
SessionID string `json:"session_id"`
Decisions []Decision `json:"decisions"`
}
SessionDecisions is the decisions extracted from one root session.
type Transcript ¶
Transcript is one root session reduced to what decision extraction needs.
func Render ¶
func Render(ctx context.Context, graph *session.Graph, redactor Redactor, opts ...Option) (*Transcript, error)
Render builds the transcript of graph's root session. Text is redacted with redactor after local paths are made repo-relative.
func (*Transcript) Text ¶
func (t *Transcript) Text() string
Text is the transcript as the extractor reads it (ARCHITECTURE.md, "decisions"). The output is deterministic for a given transcript.
type Tree ¶
type Tree struct {
// contains filtered or unexported fields
}
Tree is a session tree prepared for rendering: its paths resolved against the tree's roots and working directories, the account names its paths reveal, the calls the human rejected, and the order of its rows. Render builds the decisions transcript from one. Its exported methods let another render of the same tree, such as a validation transcript, share these rules rather than copy them.
func (*Tree) EditedPath ¶
EditedPath is the repo-relative path an edit touch wrote, as the edited-file list shows it. It reports false for a read, for a touch of a call the human rejected (the mappers record the file the agent proposed to write), for a path nothing relativizes, and, when the session's file evidence was supplied, for a path that names no stored edit or several.
func (*Tree) NotificationReport ¶
NotificationReport is the report in a task notification for one of the tree's Claude Code subagents, with the id of the call that started that subagent; both are empty for any other input.
func (*Tree) Result ¶
func (r *Tree) Result(toolUseID string) *session.ToolResult
Result is the result of a tool call, or nil when it has none.
func (*Tree) ScrubPaths ¶
ScrubPaths rewrites the tree's roots and working directories to repo-relative form, masks every other local path, and masks the account names the tree's own paths reveal where they are joined to other text: a dashed project directory, a hostname, a branch. It masks paths and account names only and redacts no secrets or e-mail addresses: its output must still go through a Redactor, as Render does, before it is shown or sent.
func (*Tree) SortRows ¶
SortRows puts rows in session order as the transcript orders a turn: by position in the vendor file when every row has one, else by timestamp when every row has one, else by model invocation; a message comes before a tool call when nothing else orders them. The order depends on which rows are given, so sort exactly the rows that will be shown.
func (*Tree) SpawnedSession ¶
SpawnedSession is the nested session a tool call spawned.
type Turn ¶
type Turn struct {
// Ordinal is the stored UserInput.Ordinal. Decision citations use it, so
// it is never renumbered; dropped inputs leave gaps.
Ordinal int `json:"ordinal"`
// Human is the cleaned prompt, empty when the input was not a human
// prompt (a system notification, an injected skill body).
Human string `json:"-"`
Events []Event `json:"-"`
// EditedFiles are the repo-relative paths edited during the turn, sorted.
// Decision `files` citations are validated against this list.
EditedFiles []string `json:"edited_files"`
}
Turn is one root-session user input and everything kept from what it caused, including its nested sessions.