api

package
v0.30.0 Latest Latest
Warning

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

Go to latest
Published: Jul 31, 2026 License: AGPL-3.0 Imports: 41 Imported by: 0

Documentation

Overview

Package api provides an HTTP API server over the derived sessions/traces/spans read model.

Index

Constants

View Source
const ProjectionSchema = "2026-06-15"

ProjectionSchema is the compatibility date of the derived projection generation currently served (the dated *_20260615 table family). It is stamped onto the wire `schema` field; a future generation bumps this in lockstep with a new dated table family (derived_projection_schemas).

Variables

This section is empty.

Functions

func CompileOpenAPI added in v0.30.0

func CompileOpenAPI(ctx context.Context, docs tapesoapi.TypeDocs) (*tapesoapi.CompiledDoc, error)

CompileOpenAPI builds the read API's published contract.

It constructs a server purely to make it register its routes, then compiles what that registration produced. Generating from the real construction path rather than from a description of it is the point: there is no second list of routes to fall out of step with the first.

The server is never started and never serves a request, so it is given no driver — the handlers it registers are function values, and nothing calls them.

func DefaultContractVersions added in v0.30.0

func DefaultContractVersions() []cassette.ContractVersion

DefaultContractVersions returns the tapes contracts this build of core serves.

This is the API server's fact to hold, and it lives next to the handlers that serve the surface it names. It is deliberately not a constant in pkg/cassette: that package is the vocabulary a cassette uses to describe itself, and a cassette declaring "I read tapes v1" must not depend on a package that also asserts which core is running. The dependency goes one way — core reads a cassette's declaration and decides.

It is a set rather than a single value so a deployment can serve a new contract while still admitting cassettes built against the previous one. That is the only way to roll a fleet of cassettes forward without a flag day, and it costs nothing to allow for now.

func NewOpenAPIParser added in v0.30.0

func NewOpenAPIParser(docs tapesoapi.TypeDocs) *tapesoapi.Parser

NewOpenAPIParser returns a parser configured the way both this server and the contract generator need it.

docs may be nil, and is nil in the running server: a deployed binary has no source tree to read prose out of. It is non-nil under `tapes dev openapi`, which points the doc reader at a checkout so the compiled document carries the prose sitting next to each field in the source.

Types

type Config

type Config struct {
	// ListenAddr is the address to listen on (e.g., ":8081")
	ListenAddr string

	// Embedder for converting query text to vectors (optional, enables MCP server)
	Embedder embeddings.Embedder

	// SpanSearcher enables GET /v1/search/spans — semantic search over
	// the span projection's embeddings (optional). Requires Embedder.
	SpanSearcher SpanSearcher

	// Pricing is the model pricing table used by /v1/sessions/summary to
	// compute per-session cost. When nil, sessions.DefaultPricing() is used.
	Pricing sessions.PricingTable

	// EnableWebUI serves the minimal browser UI at /. It is disabled by default
	// so API-only servers do not expose a human-facing development UI unless
	// explicitly requested.
	EnableWebUI bool

	// ContractVersions is the set of tapes contracts this server serves. A
	// cassette whose depends.core falls outside the set is refused at
	// admission, and the newest entry is what the discovery document
	// advertises as current. Empty means DefaultContractVersions().
	ContractVersions []cassette.ContractVersion

	// SkillLLM* configure the LLM used by POST /v1/skills/generate. They are
	// populated from the search/embedding credential so skill extraction
	// reuses the same shared key the platform already mounts for search —
	// no separate provider key. An empty Provider/APIKey falls back to the
	// generator's env/credentials resolution at call time.
	SkillLLMProvider string
	SkillLLMModel    string
	SkillLLMAPIKey   string
	SkillLLMBaseURL  string
}

Config is the API server configuration.

type Discovery added in v0.30.0

type Discovery struct {
	ContractVersion string                     `json:"contract_version"`
	Cassettes       []DiscoveryEntry           `json:"cassettes"`
	Problems        []cassetterunner.Rejection `json:"problems"`
}

Discovery is the document served at GET /v1/cassettes.

It publishes what each cassette *is* — never what it is configured to. Core holds no configuration values—the deployment supplies them directly to the cassette—so there is nothing here to leak.

type DiscoveryDepends added in v0.30.0

type DiscoveryDepends struct {
	Core  string   `json:"core"`
	Views []string `json:"views"`
}

DiscoveryDepends is a cassette's declared dependency on core.

type DiscoveryEntry added in v0.30.0

type DiscoveryEntry struct {
	Name           string             `json:"name"`
	Version        string             `json:"version,omitempty"`
	DisplayName    string             `json:"display_name,omitempty"`
	Description    string             `json:"description,omitempty"`
	RoutePrefix    string             `json:"route_prefix"`
	Depends        *DiscoveryDepends  `json:"depends,omitempty"`
	Tables         []string           `json:"tables"`
	Config         []DiscoverySetting `json:"config"`
	OpenAPIPath    string             `json:"openapi_path"`
	OpenAPIStatus  tapesoapi.Status   `json:"openapi_status"`
	ManifestDigest string             `json:"manifest_digest"`
}

DiscoveryEntry describes one served cassette.

The OpenAPI document is referenced, not inlined. A single spec runs to tens of kilobytes and clients poll discovery; inlining five of them turns every client boot into a megabyte of mostly-unchanged bytes. The digest is enough to know whether the fetch is worth making.

type DiscoverySetting added in v0.30.0

type DiscoverySetting struct {
	Key         string `json:"key"`
	Type        string `json:"type"`
	Required    bool   `json:"required"`
	Secret      bool   `json:"secret"`
	Default     any    `json:"default,omitempty"`
	Description string `json:"description,omitempty"`
}

DiscoverySetting is one configuration key as a schema, never as a value.

type MCPError added in v0.30.0

type MCPError struct {
	// Code is the JSON-RPC error code.
	Code int `json:"code" oas:"example=-32600"`

	// Message is a short description of the failure.
	Message string `json:"message" oas:"example=invalid request"`
}

MCPError is a JSON-RPC 2.0 error object.

type MCPRequest added in v0.30.0

type MCPRequest struct {
	// JSONRPC is the protocol version, always "2.0".
	JSONRPC string `json:"jsonrpc" oas:"example=2.0"`

	// ID correlates a response with this request. Absent on notifications.
	ID string `json:"id,omitempty" oas:"example=1"`

	// Method is the MCP method being invoked, such as tools/call.
	Method string `json:"method" oas:"example=tools/call"`

	// Params are the method's arguments.
	Params map[string]any `json:"params,omitempty"`
}

MCPRequest is a JSON-RPC 2.0 request to the streamable MCP endpoint.

type MCPResponse added in v0.30.0

type MCPResponse struct {
	// JSONRPC is the protocol version, always "2.0".
	JSONRPC string `json:"jsonrpc" oas:"example=2.0"`

	// ID is the id of the request this answers.
	ID string `json:"id,omitempty" oas:"example=1"`

	// Result is the method's return value on success.
	Result map[string]any `json:"result,omitempty"`

	// Error describes the failure when the call did not succeed.
	Error *MCPError `json:"error,omitempty"`
}

MCPResponse is a JSON-RPC 2.0 response from the streamable MCP endpoint.

Exactly one of Result and Error is set, which is the JSON-RPC contract rather than anything this server adds.

type MainUsage added in v0.25.0

type MainUsage struct {
	InputTokens  int64 `json:"input_tokens"`
	OutputTokens int64 `json:"output_tokens"`
}

MainUsage is the task token slice of a trace: the main agent and its subagents (call_kind=main across every thread), no cache split or cost (those live on the total Usage). Deliberately not spine-only — a subagent doing the user's work is task spend, not shadow.

type Metrics added in v0.7.0

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

Metrics is the Prometheus surface for the Tapes API server. Each Server owns its own registry so tests can scrape in isolation; the production path mounts /metrics on the Fiber app via NewServer.

func NewMetrics added in v0.7.0

func NewMetrics() *Metrics

NewMetrics constructs the Tapes API server's RED metrics. Labels stay templated (`route`) rather than per-URL so :hash path params don't blow up cardinality.

func (*Metrics) Handler added in v0.7.0

func (m *Metrics) Handler() fiber.Handler

Handler returns a Fiber handler that serves Prometheus text exposition from this Metrics instance's registry. Mount it at /metrics with no auth. The handler is built once at NewMetrics time and cached — see the scrapeHandler field comment for why.

func (*Metrics) Middleware added in v0.7.0

func (m *Metrics) Middleware() fiber.Handler

Middleware returns a Fiber handler that records request count + duration per (route template, method, status). Templates like /v1/sessions/:hash stay as the label value so the :hash path param never expands cardinality.

Register this OUTSIDE recover.New() (i.e. via app.Use before recover) — see resolveStatus for why.

func (*Metrics) Registry added in v0.7.0

func (m *Metrics) Registry() *prometheus.Registry

Registry exposes the *prometheus.Registry so tests can scrape against the same registry the middleware writes to.

type ModelUsage added in v0.16.0

type ModelUsage struct {
	Model        string  `json:"model"`
	Calls        int64   `json:"calls"`
	InputTokens  int64   `json:"input_tokens"`
	OutputTokens int64   `json:"output_tokens"`
	CostUsd      float64 `json:"cost_usd"`
}

ModelUsage is one model's contribution to a session in the API: how many llm calls ran on it and what they spent. Cost-weighted (priced at derive time) so a per-model share reflects spend, not call count.

type PayloadMode added in v0.16.0

type PayloadMode string

PayloadMode selects how much span payload a trace response carries. Full embeds the stored content verbatim; preview truncates long text so list-shaped reads stay O(structure), with the span drill-in endpoint serving the full payload on demand.

const (
	PayloadFull    PayloadMode = "full"
	PayloadPreview PayloadMode = "preview"
)

type RawTurnHeaderItem added in v0.16.0

type RawTurnHeaderItem struct {
	ID            int64           `json:"id"`
	Source        string          `json:"source"`
	Provider      string          `json:"provider,omitempty"`
	AgentName     string          `json:"agent_name,omitempty"`
	RequestID     string          `json:"request_id,omitempty"`
	ReceivedAt    time.Time       `json:"received_at"`
	Meta          json.RawMessage `json:"meta,omitempty" oas:"type=object"`
	RequestBytes  int64           `json:"request_bytes"`
	ResponseBytes int64           `json:"response_bytes"`
}

RawTurnHeaderItem is one wire-log row: what crossed the wire (or arrived as a transcript push), without the payload blobs. The `source` field is the wire-vs-transcript distinction.

type RawTurnListResponse added in v0.16.0

type RawTurnListResponse struct {
	Items []RawTurnHeaderItem `json:"items"`
}

RawTurnListResponse is a session's wire log.

type Server

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

Server is the API server for managing and querying the Tapes system

func NewServer

func NewServer(config Config, driver storage.Driver, log *slog.Logger) (*Server, error)

NewServer creates a new API server. The storer is injected to allow sharing with other components (e.g., the proxy when not run as a singleton).

func (*Server) OpenAPIParser added in v0.30.0

func (s *Server) OpenAPIParser() *tapesoapi.Parser

OpenAPIParser returns the live parser every route on this server registered itself into.

It is the single source the published contract is generated from, which is what makes "served but undocumented" impossible by construction rather than by a convention someone has to remember.

func (*Server) RefreshCassetteSpecs added in v0.30.0

func (s *Server) RefreshCassetteSpecs(ctx context.Context) []error

RefreshCassetteSpecs refreshes this server's cassette OpenAPI cache once.

func (*Server) Run

func (s *Server) Run() error

Run starts the API server on the configured address.

func (*Server) RunWithListener

func (s *Server) RunWithListener(listener net.Listener) error

RunWithListener starts the API server using the provided listener.

func (*Server) SetCassetteSources added in v0.30.0

func (s *Server) SetCassetteSources(sources []string)

SetCassetteSources configures exact full OpenAPI document URLs on this server's lifetime-owned runner.

func (*Server) Shutdown

func (s *Server) Shutdown() error

Shutdown gracefully shuts down the API server.

func (*Server) StartCassetteSpecRefresh added in v0.30.0

func (s *Server) StartCassetteSpecRefresh(ctx context.Context, interval time.Duration)

StartCassetteSpecRefresh begins this server's source-resolution lifecycle. It retries quickly during startup so sidecars can become ready alongside tapes, then settles onto the configured refresh interval.

type SessionDetailResponse added in v0.12.0

type SessionDetailResponse struct {
	Session SessionItem `json:"session"`
}

SessionDetailResponse is the response for GET /v1/sessions/:id: the session record alone. The conversation content lives on the span model (GET /v1/sessions/:id/traces).

type SessionItem added in v0.12.0

type SessionItem struct {
	// Identity — capture-side facts, ingest-written.
	ID               string         `json:"id"`
	HarnessID        string         `json:"harness_id"`
	HarnessSessionID string         `json:"harness_session_id"`
	Cwd              string         `json:"cwd,omitempty"`
	HarnessVersion   string         `json:"harness_version,omitempty"`
	ParentSessionID  string         `json:"parent_session_id,omitempty"`
	StartedAt        time.Time      `json:"started_at"`
	LastSeenAt       time.Time      `json:"last_seen_at"`
	EndedAt          *time.Time     `json:"ended_at,omitempty"`
	HarnessMetadata  map[string]any `json:"harness_metadata,omitempty"`
	// AuthSubject is the gateway-stamped JWT subject (WorkOS user id)
	// captured at ingest; empty for rows captured before the edge began
	// stamping it.
	AuthSubject string `json:"auth_subject,omitempty"`
	// Name is the harness identity-row label — the harness-supplied session
	// name (a plan slug), or the folded title (rollup.title) as a fallback
	// when no name was captured. This is capture/deriver provenance, NOT a
	// user title: ingest re-sends it every turn. Clients should render
	// DisplayTitle, not Name (PCC-970).
	Name string `json:"name,omitempty"`
	// DisplayName is the user's Console rename (sessions.display_name),
	// empty unless a user set one. Written only by PATCH /v1/sessions/:id,
	// never by ingest, so it survives a live session. It is the top of the
	// DisplayTitle resolution; exposed raw so the edit affordance can seed
	// its input from the user's own title (not the resolved fallback).
	DisplayName string `json:"display_name,omitempty"`
	// DisplayTitle is the server-resolved label clients should render:
	// DisplayName -> rollup.title (generated) -> preview -> Name -> id
	// slice. Resolving once on the server keeps every client (Console,
	// paper CLI) from re-deriving — and diverging on — the precedence
	// (PCC-970). Never empty: it falls back to a short harness id slice, then
	// the session id (the primary key, always set for a stored row).
	DisplayTitle string `json:"display_title"`
	// Live is a runtime presence signal, not a projection fact: true when
	// the session has no recorded end and was seen within the liveness
	// window. Keyed on ended_at + last_seen_at recency (both ingest-fresh),
	// never on the derived status: an interactive session folds to a
	// terminal status (an end_turn assistant reply reads as "completed")
	// after every turn while still open, so status cannot gate liveness.
	// Computed at response time so the console renders it directly instead
	// of inferring "running" itself.
	Live bool `json:"live"`
	// Rollup is the deriver-owned projection over the session's spans.
	Rollup SessionRollup `json:"rollup"`
}

SessionItem is the per-session shape: capture identity at the top level, the deriver-owned projection nested under `rollup`. The split mirrors the storage rows — identity is ingest-written, rollup is deriver-written — so the wire can't blur which layer owns a field.

type SessionListResponse added in v0.4.0

type SessionListResponse struct {
	Items      []SessionItem `json:"items"`
	NextCursor string        `json:"next_cursor,omitempty"`
}

SessionListResponse is the response envelope for GET /v1/sessions.

type SessionRollup added in v0.25.0

type SessionRollup struct {
	Status string `json:"status"`
	// Title is the deriver's folded session title (derived_title),
	// generated from the conversation. Empty until title generation
	// produces one. It never falls back to the identity-row name, so it is
	// the stable descriptive title clients prefer for display; the
	// identity-row label (harness name or rename) is SessionItem.Name.
	Title     string `json:"title,omitempty"`
	Preview   string `json:"preview,omitempty"`
	TurnCount int    `json:"turn_count"`
	// Model is the dominant conversation-spine model; ModelUsage is the
	// per-model spend breakdown across every thread (subagent models
	// included), cost-ordered so the UI can show "dominant model + share"
	// without a cheap-subagent fan-out skewing it.
	Model      string       `json:"model,omitempty"`
	ModelUsage []ModelUsage `json:"model_usage,omitempty"`
	// KindCounts (spans per call_kind) and Tasks (TaskCreate/TaskUpdate
	// folds) are pinned so the rollup shape is uniform across sessions.
	KindCounts map[string]int `json:"kind_counts"`
	Tasks      []TreeTask     `json:"tasks"`
	Usage      SessionUsage   `json:"usage"`
}

SessionRollup is the deriver-owned session projection — status, title, counts, and spend, all folded from the span layer at derive time. Every field is 'unknown'/zero/empty until the session first derives.

type SessionTracesResponse added in v0.16.0

type SessionTracesResponse struct {
	Schema  string         `json:"schema"`
	Session SessionItem    `json:"session"`
	Traces  []TraceDetail  `json:"traces"`
	Links   []SpanLinkItem `json:"links"`
}

SessionTracesResponse is the composite session view on the span model. `schema` stamps the projection generation the rows were derived against, so the presentational shape can version independently.

func BuildSessionTraces added in v0.16.0

func BuildSessionTraces(
	session SessionItem,
	turns []storage.SpanTurnRecord,
	spans []storage.SpanRecord,
	links []storage.SpanLinkRecord,
	mode PayloadMode,
) *SessionTracesResponse

BuildSessionTraces assembles the composite response. Pure rendering: every edge and kind here was computed by the deriver. Exported so `tapes dev trace-fixtures` emits byte-identical JSON to the handler.

type SessionUsage added in v0.25.0

type SessionUsage struct {
	InputTokens  int64   `json:"input_tokens"`
	OutputTokens int64   `json:"output_tokens"`
	CostUSD      float64 `json:"cost_usd"`
}

SessionUsage is the session's total token/cost spend, folded from the span layer. Pinned (no omitempty) for a uniform object shape.

type SpanItem added in v0.16.0

type SpanItem struct {
	TraceID      string `json:"trace_id"`
	SpanID       string `json:"span_id"`
	ParentSpanID string `json:"parent_span_id,omitempty"`
	// Seq is the span's presentation ordinal within its trace; spans
	// arrive sorted by it (started_at ties inside one llm call — parallel
	// tool batches share an instant).
	Seq        int64     `json:"seq"`
	Kind       string    `json:"kind"`
	Name       string    `json:"name"`
	Status     string    `json:"status"`
	StartedAt  time.Time `json:"started_at"`
	DurationNS int64     `json:"duration_ns"`
	// Deriver-written taxonomy, promoted from the old metadata grab-bag.
	CallKind   string `json:"call_kind"`
	Model      string `json:"model"`
	StopReason string `json:"stop_reason"`
	ThreadID   string `json:"thread_id"`
	RawTurnID  int64  `json:"raw_turn_id,omitempty"`
	// Verdict is the typed security-monitor disposition (null off
	// permission-check spans), deriver-written. It is a Verdict object or
	// null on the wire; the oas tag states that, because a json.RawMessage
	// carries no shape a reflector could recover.
	Verdict json.RawMessage `json:"verdict" oas:"type=object,nullable"`
	// Input/Output are content-block arrays (llm.ContentBlock), uniform for
	// every kind (tool spans included — no unwrapping). Pinned to [] when
	// empty.
	Input  json.RawMessage `json:"input" oas:"type=array:object"`
	Output json.RawMessage `json:"output" oas:"type=array:object"`
	// Usage (was `metrics`) is an llm.Usage object on the wire — {}-pinned
	// for usage-less spans.
	Usage json.RawMessage `json:"usage" oas:"type=object"`
	// Payload marks a preview-truncated span so the console drills in for
	// the full payload; absent in full mode.
	Payload string `json:"payload,omitempty"`
}

SpanItem is one observed unit of work. Every field is a deriver output, formatting-only: the harness-taxonomy fields (call_kind, model, stop_reason, thread_id, verdict) are typed rather than bagged in a metadata map, and input/output are uniform content-block arrays for ALL kinds — the console owns per-kind rendering.

type SpanLinkItem added in v0.16.0

type SpanLinkItem struct {
	Kind        string `json:"kind"`
	FromTraceID string `json:"from_trace_id"`
	FromSpanID  string `json:"from_span_id"`
	FromIO      string `json:"from_io,omitempty"`
	ToTraceID   string `json:"to_trace_id"`
	ToSpanID    string `json:"to_span_id"`
	ToIO        string `json:"to_io,omitempty"`
}

SpanLinkItem is a dataflow edge. kind is a typed top-level field (rejoin / verdict / compaction-seam / emits / feeds); from/to trace ids differ on cross-trace causality.

type SpanSearchOutput added in v0.16.0

type SpanSearchOutput struct {
	Query   string             `json:"query"`
	Results []SpanSearchResult `json:"results"`
	Count   int                `json:"count"`
}

SpanSearchOutput is the span search response.

type SpanSearchResult added in v0.16.0

type SpanSearchResult struct {
	TraceID   string  `json:"trace_id"`
	SpanID    string  `json:"span_id"`
	SessionID string  `json:"session_id,omitempty"`
	Score     float32 `json:"score"`
	// UserPrompt is the prompt of the turn (trace) the span belongs to.
	// Served explicitly (not omitempty) so a synthetic turn's empty prompt
	// reaches consumers as "" rather than a dropped key — see TraceItem.
	UserPrompt string `json:"user_prompt"`
	// Snippet previews the matched span's delta-only text.
	Snippet   string    `json:"snippet,omitempty"`
	Model     string    `json:"model,omitempty"`
	StartedAt time.Time `json:"started_at"`
}

SpanSearchResult is one span hit with its trace/turn context.

type SpanSearcher added in v0.16.0

type SpanSearcher interface {
	Search(ctx context.Context, orgID string, embedding []float32, topK int) ([]spanembed.Hit, error)
}

SpanSearcher performs vector-similarity search over span embeddings. *spanembed.Store implements it; tests substitute fakes.

type StatsResponse added in v0.4.0

type StatsResponse struct {
	SessionCount    int     `json:"session_count"`
	TurnCount       int     `json:"turn_count"`
	CompletedCount  int     `json:"completed_count"`
	TotalCost       float64 `json:"total_cost"`
	InputTokens     int64   `json:"input_tokens"`
	OutputTokens    int64   `json:"output_tokens"`
	TotalDurationMs int64   `json:"total_duration_ms"`
	ToolCalls       int     `json:"tool_calls"`
}

StatsResponse is the response for GET /v1/stats.

The numbers come from the span-projection trace-grain rollups, so they agree with the session detail and trace views:

  • InputTokens / OutputTokens / TotalCost are SUMs of span_turns rollups — delta-only per-call usage, never the re-sent history (each main call re-bills the whole conversation on the wire).
  • TotalDurationMs is the SUM of trace durations — agent time. Idle time between turns does not count. Served in milliseconds, not the nanoseconds we store: the summed ns over a wide window overflows a JSON consumer's 2^53 safe-integer range (~104 cumulative days), and sub-ms precision is meaningless for an aggregate agent-time figure.
  • TurnCount counts traces (user-visible turns).
  • CompletedCount counts distinct sessions whose denormalized derived_status is 'completed' (chain-aware, PCC-515).

type TraceDetail added in v0.16.0

type TraceDetail struct {
	Schema string         `json:"schema,omitempty"`
	Trace  TraceItem      `json:"trace"`
	Spans  []SpanItem     `json:"spans"`
	Links  []SpanLinkItem `json:"links,omitempty"`
}

TraceDetail is one trace with its spans. In the composite session response links are session-scoped (top level); the single-trace endpoint sets Links to the edges touching that trace. `schema` stamps the projection generation on the STANDALONE /v1/traces/{id} response (omitempty — the composite embeds TraceDetail and already carries one stamp at the top level, so the embedded copies stay unstamped).

func BuildTraceDetail added in v0.16.0

func BuildTraceDetail(turn storage.SpanTurnRecord, spans []storage.SpanRecord, links []storage.SpanLinkRecord, mode PayloadMode) TraceDetail

BuildTraceDetail renders one turn with its spans and links. Exported so `tapes dev trace-fixtures` emits byte-identical JSON to the handler.

type TraceItem added in v0.16.0

type TraceItem struct {
	TraceID string `json:"trace_id"`
	// UserPrompt is served explicitly (not omitempty): a synthetic opener
	// has an empty prompt, and dropping the key turns the empty string
	// into `undefined` on the wire, which breaks consumers that expect a
	// string (e.g. the console's stripHarnessTags). Empty means synthetic.
	UserPrompt string `json:"user_prompt"`
	// ResponsePreview is the derive-time fold of the closing
	// conversation-spine llm call's text output — the answer line for
	// collapsed turn cards, so summary consumers never need spans.
	ResponsePreview string `json:"response_preview,omitempty"`
	Status          string `json:"status"`
	// Source is the capture origin of the turn's rows ("wire" |
	// "transcript"), promoted from raw_turns.source. Per-trace, so a
	// session can mix live wire capture and transcript backfill. Today
	// every trace is "wire": transcripts only reconcile fork/parent edges
	// during derivation, they never form a trace on their own. "transcript"
	// becomes real when a session is reconstructed purely from a transcript
	// file with no proxy capture (an OSS backfill path).
	Source     string     `json:"source"`
	StartedAt  time.Time  `json:"started_at"`
	EndedAt    *time.Time `json:"ended_at,omitempty"`
	DurationNS int64      `json:"duration_ns"`
	SpanCount  int        `json:"span_count"`
	// Usage is the trace's total token/cost spend over ALL llm spans,
	// shadow calls included; MainUsage is the task slice — the main agent
	// and its subagents (every call_kind=main span, across threads). The
	// difference (Usage − MainUsage) is the harness's shadow spend
	// (permission checks, title-gen, web summaries) on the turn.
	Usage     TraceUsage `json:"usage"`
	MainUsage MainUsage  `json:"main_usage"`
	// Synthetic is a typed deriver signal ("post-compaction" for a
	// compaction continuation, "shadow-opener" for a shadow-only opener),
	// promoted out of the old metadata grab-bag. Absent for genuine
	// prompt-opened turns.
	Synthetic string `json:"synthetic,omitempty"`
}

TraceItem is one user-visible turn's header. session_id / harness ids are not duplicated here — they belong to the session. A trace's post-compaction status is the typed Synthetic field below (promoted out of the old metadata grab-bag); the same seam is also recoverable from the session's compaction-seam links.

type TraceListResponse added in v0.16.0

type TraceListResponse struct {
	Schema string      `json:"schema"`
	Items  []TraceItem `json:"items"`
}

TraceListResponse is the summaries list for one session. `schema` stamps the projection generation the rows were derived against — the same stamp the composite carries — so every trace-grain response is self-describing, not just the composite.

func BuildTraceList added in v0.16.0

func BuildTraceList(rows []storage.TraceSummaryRecord) TraceListResponse

BuildTraceList renders the turn-summary rows for one session. Exported so `tapes dev trace-fixtures` emits byte-identical JSON to the handler.

type TraceUsage added in v0.25.0

type TraceUsage struct {
	InputTokens         int64   `json:"input_tokens"`
	OutputTokens        int64   `json:"output_tokens"`
	CacheReadTokens     int64   `json:"cache_read_tokens"`
	CacheCreationTokens int64   `json:"cache_creation_tokens"`
	CostUSD             float64 `json:"cost_usd"`
}

TraceUsage is a trace's total token/cost rollup. Fields are pinned (no omitempty) so the object shape is uniform across traces.

type TreeTask added in v0.16.0

type TreeTask struct {
	ID          string `json:"id"`
	Subject     string `json:"subject"`
	Description string `json:"description,omitempty"`
	Status      string `json:"status"`
	Updates     int    `json:"updates"`
}

TreeTask is one task folded from the session's TaskCreate/TaskUpdate calls.

Directories

Path Synopsis
Package cassetterunner resolves and serves the cassettes an API server publishes.
Package cassetterunner resolves and serves the cassettes an API server publishes.
Package mcp provides an MCP (Model Context Protocol) server for the Tapes system.
Package mcp provides an MCP (Model Context Protocol) server for the Tapes system.

Jump to

Keyboard shortcuts

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