wire

package
v0.14.0 Latest Latest
Warning

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

Go to latest
Published: Aug 21, 2026 License: MIT Imports: 3 Imported by: 0

Documentation

Overview

Package wire is the serialized form of a search answer — the JSON contract MCP's search tool and the CLI's --json output both speak. It lives beside the search service for the same reason the service exists: two surfaces that answer the same query must not drift into describing that answer differently.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type CompactFileGroup added in v0.14.0

type CompactFileGroup struct {
	FilePath  string              `json:"file_path"`
	Namespace string              `json:"namespace,omitempty"`
	Hits      []CompactResultItem `json:"hits"`
}

CompactFileGroup groups compact hits under the one copy of their file path. @intent avoid repeating file identity on every hit while keeping federated namespace labels.

type CompactResponse added in v0.14.0

type CompactResponse struct {
	Files              []CompactFileGroup `json:"files"`
	WeakFiltered       int                `json:"weak_filtered"`
	Truncated          bool               `json:"truncated"`
	PoolTruncated      bool               `json:"pool_truncated"`
	Limits             Limits             `json:"limits"`
	AnnotationCoverage evidence.Coverage  `json:"annotation_coverage"`
	Next               []NextAction       `json:"next,omitempty"`
	Note               string             `json:"note,omitempty"`
}

CompactResponse is the token-efficient search view used by agents that do not need storage ids or redundant per-hit names and paths. @intent preserve search decisions, completion signals, and continuations in a smaller wire payload.

type CompactResultItem added in v0.14.0

type CompactResultItem struct {
	QualifiedName string           `json:"qualified_name"`
	Kind          graph.NodeKind   `json:"kind"`
	StartLine     int              `json:"start_line"`
	EndLine       int              `json:"end_line"`
	Intent        string           `json:"intent,omitempty"`
	Matched       []evidence.Match `json:"matched,omitempty"`
	Reason        string           `json:"reason,omitempty"`
	MatchedTerms  []string         `json:"matched_terms,omitempty"`
}

CompactResultItem keeps the evidence an agent needs to choose and open a hit, without repeating data already present in its file group. @intent reduce search-response context while preserving rank evidence and exact source bounds.

type FileGroup

type FileGroup struct {
	FilePath  string       `json:"file_path"`
	Namespace string       `json:"namespace,omitempty"`
	HitCount  int          `json:"hit_count"`
	Hits      []ResultItem `json:"hits"`
}

FileGroup is one file and every hit it answered the query with.

The file is the unit of a search answer because it is the unit of reading: a caller picks a file first and a declaration inside it second. Grouping is also what let the old per-file cap go — ten hits in one file cost the reader one decision, not ten — so a file that appears here appears whole.

@intent let a caller choose between files, then read inside the one it chose.

type Limits

type Limits struct {
	Files     int `json:"files"`
	Offset    int `json:"offset"`
	HitBudget int `json:"hit_budget"`
}

Limits states the bounds that shaped this page, all counted in files except the budget, which only decides whether one more file joins. @intent let a caller tell a short answer from the first page of a long one.

type NextAction

type NextAction struct {
	Reason string         `json:"reason"`
	Tool   string         `json:"tool,omitempty"`
	Args   map[string]any `json:"args,omitempty"`
	// Skill names a packaged workflow to run rather than a tool to call. It is
	// set only for the step no tool can perform.
	Skill string `json:"skill,omitempty"`
}

NextAction is one step that widens this answer, written out so an agent can take it without inventing anything.

Before this existed a response could say it withheld things and leave the caller with no way to reach them. Naming the tool and its arguments turns a dead-end count into a step.

Most steps are a call: Tool and Args. One is not, and cannot be — when the answer was empty because nobody ever recorded a reason, the fix is to write those reasons, which is reading and judgement rather than a query. That step names a Skill instead, and exactly one of the two forms is ever filled in.

@domainRule an action names either a Tool with its Args or a Skill, never both, so a caller never has to guess which one to act on. @intent turn what a search withheld into a step the caller can actually take.

type Response

type Response struct {
	Files        []FileGroup `json:"files"`
	FileCount    int         `json:"file_count"`
	WeakFiltered int         `json:"weak_filtered"`
	// Truncated is true when this answer did not reach every file the query
	// answered with. It is never about hits: a shown file is shown whole.
	Truncated bool `json:"truncated"`
	// PoolTruncated is true when the candidate pool behind this answer came back
	// full, so the backend held at least as many candidates as the pool could
	// take and may have held more.
	//
	// It is a second signal rather than part of Truncated because the two say
	// different things and call for different moves. Truncated counts files this
	// page did not reach, and stays about files. PoolTruncated says the page
	// stopped at the edge of what was fetched. `truncated: false` with
	// `pool_truncated: true` is the case that used to read as "that is
	// everything" when it was not: ask for the next page anyway.
	PoolTruncated bool   `json:"pool_truncated"`
	Limits        Limits `json:"limits"`
	// AnnotationCoverage is how many of the searched declarations recorded a
	// reason — an @intent or a @domainRule — out of how many were indexed.
	//
	// It is on every answer, not only the empty ones, because it is what makes an
	// answer's size readable: two files out of a repository where four hundred
	// declarations recorded a reason is a thin answer, and two out of a repository
	// where six did is most of what there was to find. `with_reason: 0` says the
	// question was put to an index nobody has written anything into yet.
	AnnotationCoverage evidence.Coverage `json:"annotation_coverage"`
	Next               []NextAction      `json:"next,omitempty"`
	Note               string            `json:"note,omitempty"`
}

Response wraps the file list so an empty answer can still say why.

It replaced a bare JSON array. An array has nowhere to put the reason a list came back short, and a caller that reads `[]` cannot tell "nothing was indexed under those words" from "everything found was unjustifiable".

@intent make a search answer self-describing, including when it is empty.

func NewResponse

func NewResponse(list evidence.List, query string, limit, offset int, withNamespace bool) Response

NewResponse converts an evidence list into the wire payload. @requires withNamespace is true only for federated searches, where a caller needs to know which repository a hit came from. @requires query, limit and offset are the ones this list was built with, so the suggested calls repeat the caller's own request. @ensures Next is empty exactly when the answer withheld nothing and had nothing to explain about coming back empty. @intent keep one conversion so no two search surfaces can drift apart.

func (Response) Compact added in v0.14.0

func (r Response) Compact() CompactResponse

Compact returns a smaller view without changing the full response contract. Search continuations retain compact mode so later pages do not grow again. @ensures every search action in Next carries compact=true. @intent let CLI and MCP share one loss-aware compact representation.

type ResultItem

type ResultItem struct {
	ID            uint           `json:"id"`
	QualifiedName string         `json:"qualified_name"`
	Kind          graph.NodeKind `json:"kind"`
	Name          string         `json:"name"`
	FilePath      string         `json:"file_path"`
	StartLine     int            `json:"start_line"`
	EndLine       int            `json:"end_line"`
	// Intent is the node's own @intent tag, empty when nobody wrote one.
	Intent string `json:"intent,omitempty"`
	// Matched names the signals this query touched — name, path, intent — so a
	// caller can tell an exact identifier hit from a hit on a written purpose.
	Matched []evidence.Match `json:"matched"`
	// Reason is the recorded reason the intent index answered with — set only
	// when this hit came from the intent query, so its absence means the hit
	// earned its place on name, path, or token overlap alone.
	Reason string `json:"reason,omitempty"`
	// MatchedTerms are the query terms written in Reason — the proof behind an
	// intent match that token overlap alone cannot see.
	MatchedTerms []string `json:"matched_terms,omitempty"`
	Namespace    string   `json:"namespace,omitempty"`
}

ResultItem summarizes one node hit returned by full-text search. @intent preserve a stable per-item DTO for search responses. @domainRule Namespace is set only in federated (multi-namespace) mode so single-namespace responses stay unchanged.

Jump to

Keyboard shortcuts

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