transcript

package
v0.2.0-rc.1 Latest Latest
Warning

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

Go to latest
Published: Nov 11, 2026 License: MIT Imports: 15 Imported by: 0

Documentation

Overview

Package transcript shapes one session graph into the transcript the viewer's session card draws as a conversation: the session's human prompts in order, what each of them caused and the agent's text replies to it, and per prompt the agent activity (its messages, model calls and tool calls with their input and output) that the hosted service serves one prompt at a time and the reference viewer host serves in the same answer.

It bounds the result, so a session of many megabytes does not stall the page drawing it: every text is cut to a limit, each prompt lists a limited number of messages and calls, and once the encoded transcript reaches its byte budget nothing after it is listed. Each cut is reported, so the reader is told. It reads the graph as recorded and passes each text it keeps, before cutting it, through the caller's Clean (a redactor), so a secret is never cut in two where the redactor can no longer see it; the caller redacts the encoded result as a whole as well.

The prompt and ownership rules are the hosted service's: a prompt is a top-level user input of kind prompt or slash command from a human with text left once the wrapper constructs are stripped (prompttext), and a nested session's activity belongs to the prompt whose tool call spawned it. Activity under a system input, such as a subagent's report, or one the harness wrote under the person's name continues the prompt before it. Activity under any other input belongs to the session and to no prompt.

Index

Constants

This section is empty.

Variables

View Source
var DefaultLimits = Limits{
	Bytes:     4 << 20,
	Prompt:    32 << 10,
	Message:   16 << 10,
	Tool:      4 << 10,
	Messages:  100,
	ToolCalls: 200,
	Reply:     4 << 10,
	Replies:   10,
	Field:     1 << 10,
	Models:    50,
}

DefaultLimits keeps a transcript to a few megabytes of JSON, which a browser parses and draws without a pause.

Functions

func Encode

func Encode(t Transcript) ([]byte, error)

Encode writes t as JSON without HTML escaping, the form Bytes bounds.

func UserHome

func UserHome() string

UserHome is the home directory: $HOME (or its platform's equivalent), else the account's, else none.

Types

type Activity

type Activity struct {
	Messages          []Message `json:"messages"`
	MessagesTruncated bool      `json:"messages_truncated"`
	// Invocations holds the first model call only, which is all the card
	// reads; InvocationsTotal counts them.
	Invocations        []Invocation `json:"invocations"`
	InvocationsTotal   int          `json:"invocations_total"`
	TokenUsage         TokenUsage   `json:"token_usage"`
	ToolCalls          []ToolCall   `json:"tool_calls"`
	ToolCallsTruncated bool         `json:"tool_calls_truncated"`
}

Activity is what the agent did for one prompt.

type Effects

type Effects struct {
	FilesRead    int `json:"files_read"`
	FilesEdited  int `json:"files_edited"`
	Reads        int `json:"reads"`
	FilesChanged int `json:"files_changed"`
	DiffHunks    int `json:"diff_hunks"`
	AddedLines   int `json:"added_lines"`
	RemovedLines int `json:"removed_lines"`
	ToolCalls    int `json:"tool_calls"`
	ExecuteCalls int `json:"execute_calls"`
	FailedCalls  int `json:"failed_calls"`
}

Effects is what a prompt, or the session, caused.

type Invocation

type Invocation struct {
	Model            string     `json:"model"`
	InputTokens      int64      `json:"input_tokens"`
	OutputTokens     int64      `json:"output_tokens"`
	CacheReadTokens  int64      `json:"cache_read_tokens"`
	CacheWriteTokens int64      `json:"cache_write_tokens"`
	StartedAt        *time.Time `json:"started_at"`
}

Invocation is one model call.

type Limits

type Limits struct {
	// Bytes is the budget for the transcript as Encode writes it.
	Bytes int
	// Prompt, Message and Tool cut one prompt, one agent message, and one
	// tool call's input or output.
	Prompt, Message, Tool int
	// Messages and ToolCalls cap how many of each one prompt's activity
	// lists.
	Messages, ToolCalls int
	// Reply cuts the preview of one text reply, and Replies caps how many
	// replies (a prompt's last) a prompt carries. A reply's whole text, up to
	// Message, is among the activity's messages.
	Reply, Replies int
	// Field cuts each of the session's own texts (title, branch, working
	// directory, agent and model names), and Models caps how many model
	// names it lists, so the part of the transcript outside the prompts is
	// bounded too.
	Field, Models int
}

Limits bound a transcript. Text limits are in bytes, cut at a character.

type Message

type Message struct {
	Text       string     `json:"text"`
	TextLength int        `json:"text_length"`
	Truncated  bool       `json:"truncated"`
	Timestamp  *time.Time `json:"timestamp"`
	Model      string     `json:"model"`
}

Message is one agent message with text, or a prompt's reply.

type Meta

type Meta struct {
	ID, Vendor, Branch string
	// Commits is how many of the pull request's commits it is linked to, and
	// Confidence the strongest of those links.
	Commits    int
	Confidence float64
}

Meta is what the graph does not say about a session: which agent ran it and how it is linked to the pull request.

type Prompt

type Prompt struct {
	TurnID          string     `json:"turn_id"`
	Ordinal         int        `json:"ordinal"`
	StartedAt       *time.Time `json:"started_at"`
	Text            string     `json:"text"`
	TextLength      int        `json:"text_length"`
	Truncated       bool       `json:"truncated"`
	Effects         *Effects   `json:"effects"`
	CostUSD         *float64   `json:"cost_usd"`
	CostIsFloor     bool       `json:"cost_is_floor"`
	Tokens          int64      `json:"tokens"`
	EstimatedTokens int64      `json:"estimated_tokens"`
	// Replies are the prompt's last text replies, oldest first, each cut to
	// Limits.Reply; RepliesOmitted counts the earlier ones left out.
	Replies        []Message `json:"replies"`
	RepliesOmitted int       `json:"replies_omitted"`
}

Prompt is one human prompt.

type Renderer

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

Renderer writes a session's transcript as the local servers serve it: the review page's linked sessions (the reference viewer host and the app) and the app's live sessions both answer with what Render returns, so the two cannot differ in what they mask. It is safe for concurrent use.

func NewRenderer

func NewRenderer(home string) *Renderer

NewRenderer returns a renderer that writes home as ~; an empty home is UserHome's.

func (*Renderer) Mask

func (r *Renderer) Mask(ctx context.Context, text string) string

Mask masks one line of text as Render masks a transcript's: secrets and e-mail addresses, with the same engine, and the home directory as ~. A line the engine cannot read is none. A line masked before is not scanned again.

func (*Renderer) Render

func (r *Renderer) Render(ctx context.Context, graph *session.Graph, meta Meta) ([]byte, error)

Render shapes and bounds the graph first (DefaultLimits), so nothing past the limits is ever redacted or held, then redacts what is left: each text as it is kept, before it is cut, and the encoded transcript as a whole, every string and key of it, with the engine "locallink.RedactGraph" uses. The home directory is written as ~ last.

type Session

type Session struct {
	RunID      string     `json:"run_id"`
	Source     string     `json:"source"`
	Title      string     `json:"title"`
	GitBranch  string     `json:"git_branch"`
	WorkingDir string     `json:"working_dir"`
	Models     []string   `json:"models"`
	StartedAt  *time.Time `json:"started_at"`
	EndedAt    *time.Time `json:"ended_at"`
	Outcome    string     `json:"outcome"`
	// UserEmail is always empty: this machine does not say who typed.
	UserEmail        string   `json:"user_email"`
	TurnCount        int      `json:"turn_count"`
	CommitCount      int      `json:"commit_count"`
	Confidence       float64  `json:"confidence"`
	Prompts          []Prompt `json:"prompts"`
	PromptsTotal     int      `json:"prompts_total"`
	PromptsTruncated bool     `json:"prompts_truncated"`
	// RepliesIncluded is always true: every prompt carries its replies.
	RepliesIncluded bool     `json:"replies_included"`
	Totals          *Effects `json:"totals"`
	Unattributed    *Effects `json:"unattributed"`
	Tokens          int64    `json:"tokens"`
	EstimatedTokens int64    `json:"estimated_tokens"`
	// CostPerPrompt is false when usage was reported only for the whole
	// session; the name is the card's. No cost is ever given (CostUSD is
	// null and CostIsFloor false): this machine has no prices.
	CostPerPrompt bool     `json:"cost_per_prompt"`
	CostUSD       *float64 `json:"cost_usd"`
	CostIsFloor   bool     `json:"cost_is_floor"`
	HasInnerTrace bool     `json:"has_inner_trace"`
}

Session is the session card's session.

type TokenUsage

type TokenUsage struct {
	Tokens              int64    `json:"tokens"`
	EstimatedTokens     int64    `json:"estimated_tokens"`
	UncachedInputTokens int64    `json:"uncached_input_tokens"`
	OutputTokens        int64    `json:"output_tokens"`
	CacheReadTokens     int64    `json:"cache_read_tokens"`
	CacheWriteTokens    int64    `json:"cache_write_tokens"`
	UnclassifiedTokens  int64    `json:"unclassified_tokens"`
	APICalls            int64    `json:"api_calls"`
	Models              []string `json:"models"`
	// contains filtered or unexported fields
}

TokenUsage sums one prompt's model usage.

type ToolCall

type ToolCall struct {
	ID              string     `json:"id"`
	Name            string     `json:"name"`
	Verb            string     `json:"verb"`
	Input           string     `json:"input"`
	InputTruncated  bool       `json:"input_truncated"`
	Output          *string    `json:"output"`
	OutputTruncated bool       `json:"output_truncated"`
	Status          string     `json:"status"`
	Timestamp       *time.Time `json:"timestamp"`
}

ToolCall is one tool call, with its result's output.

type Transcript

type Transcript struct {
	Session Session `json:"session"`
	// Activity is each listed prompt's activity, by its turn id.
	Activity map[string]Activity `json:"activity"`
	// Truncated says some prompts, messages, calls or text were left out or
	// cut; LimitBytes is the budget that was applied to the encoded size.
	Truncated  bool `json:"truncated"`
	LimitBytes int  `json:"limit_bytes"`
}

Transcript is the answer for one session. Its JSON names follow the viewer's transcript view model (oss/viewer/src/transcript.ts).

func Build

func Build(g *session.Graph, meta Meta, limits Limits, clean func(string) (string, error)) (Transcript, error)

Build shapes g, whose top-level session is the one meta describes, within limits. A graph with no top-level session yields a transcript with no prompts.

clean, when set, is given each text the transcript keeps before it is cut (and cut first to the limit plus cleanMargin), and must return a text of the same length, as a redactor does. An error from it ends the build.

Jump to

Keyboard shortcuts

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