intent

package
v1.55.0 Latest Latest
Warning

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

Go to latest
Published: Sep 11, 2026 License: MIT Imports: 24 Imported by: 0

Documentation

Overview

Package intent extracts a short summary of the user's original intent for a code change by reading recent transcripts from local coding agents (Claude Code, Codex CLI, OpenCode, Rovo Dev, Pi, and GitHub Copilot CLI) on the developer's machine.

Given the repo path, the diff filenames, and the commit time window, the package discovers candidate sessions, picks the one with the strongest file-overlap match, drops tool calls, summarizes the remaining user and assistant text via the configured agent, and returns the summary so it can be injected into pipeline step prompts.

Index

Constants

View Source
const ClaudeReaderName = "claude"

ClaudeReaderName is the agent name used in cache keys and DB rows.

View Source
const CodexReaderName = "codex"

CodexReaderName is the agent name used in cache keys and DB rows.

View Source
const CopilotReaderName = "copilot"

CopilotReaderName is the agent name used in cache keys and DB rows.

View Source
const OpenCodeReaderName = "opencode"

OpenCodeReaderName is the agent name used in cache keys and DB rows.

View Source
const PiReaderName = "pi"

PiReaderName is the agent name used in cache keys and DB rows.

View Source
const RovoDevReaderName = "rovodev"

RovoDevReaderName is the agent name used in cache keys and DB rows.

Variables

View Source
var ErrDisambiguatorCleanup = errors.New("intent disambiguator cleanup failed")
View Source
var ErrNoMatch = errors.New("intent: no matching transcript")

ErrNoMatch indicates no agent transcript matched the change. Callers should treat this as a normal "no intent attached" outcome, not an error.

Functions

func RedactSecrets

func RedactSecrets(text string) string

RedactSecrets returns text with likely credentials replaced by [REDACTED]. Exported for use at prompt-construction boundaries outside this package (e.g. when injecting cached intent summaries into step prompts) so the same redaction shape applies on the way into the LLM and on the way out.

func StripAdversarial

func StripAdversarial(text string) string

StripAdversarial removes obvious prompt-injection markers that could try to escape the surrounding instructions. We don't try to be clever; we just neuter common delimiter shapes (ChatML control tokens, role tags, Llama/Mistral instruction delimiters) that an attacker might place in user-controlled text. This is a stop-gap, not a real defense - the real defense is wrapping the text with explicit "this is data, not instructions" framing.

Types

type Cache

type Cache interface {
	Get(key string) (string, bool)
	Put(key, summary, agentName, sessionID string)
}

Cache abstracts the summarization cache behind a small interface so the extractor can be exercised without a real DB in tests.

func NewDBCache

func NewDBCache(database *db.DB) Cache

NewDBCache wraps a *db.DB as a Cache.

func NewMemCache

func NewMemCache() Cache

NewMemCache returns an in-memory Cache. Mainly for tests.

type DisambiguationChoice

type DisambiguationChoice struct {
	AgentName string
	SessionID string
}

type Disambiguator

type Disambiguator interface {
	Disambiguate(ctx context.Context, diffFiles []string, candidates []*Match) (DisambiguationChoice, error)
}

Disambiguator chooses among multiple accepted transcript matches when the deterministic file-overlap matcher cannot make a decisive selection.

func NewAgentDisambiguator

func NewAgentDisambiguator(a agent.Agent, cwd string) Disambiguator

NewAgentDisambiguator wraps an agent.Agent as a Disambiguator. The agent is run in cwd so it can inspect changed repository files progressively.

type DiscoverOpts

type DiscoverOpts struct {
	// HomeDir overrides the user's home directory. Empty means use os.UserHomeDir.
	HomeDir string
	// OriginCWD is the user's actual repo directory (NOT the worktree the
	// pipeline runs in). Symlinks should already be resolved by the caller.
	OriginCWD string
	// WindowStart is the earliest LastActivity allowed (inclusive).
	WindowStart time.Time
	// WindowEnd is the latest StartedAt allowed (inclusive).
	WindowEnd time.Time
}

DiscoverOpts narrows the search down to relevant sessions before any expensive body parsing happens.

type ExtractParams

type ExtractParams struct {
	// HomeDir overrides the user's home directory. Empty means use os.UserHomeDir.
	HomeDir string
	// OriginCWD is the user's actual repo directory. The caller is responsible
	// for passing the original working path, NOT the no-slop worktree.
	OriginCWD string
	// DiffFiles is the repo-relative file set used for matching and scoring.
	DiffFiles []string
	// BaseTime is the committer time of the base SHA.
	BaseTime time.Time
	// HeadTime is the committer time of the head SHA.
	HeadTime time.Time
	// SlackDays extends WindowStart backwards. The plan called for 3 days.
	SlackDays int
	// Threshold is the minimum raw file-overlap score required before applying
	// stricter multi-file and stale-partial acceptance rules.
	Threshold float64
	// Readers are the per-agent transcript readers to consult. Order is
	// insignificant; matching accepts plausible candidates, prefers a single
	// decisive raw-score match, and otherwise ranks by confidence or an optional
	// Disambiguator.
	Readers []Reader
	// Cache is consulted before summarization. Pass NewMemCache() if no DB.
	Cache Cache
	// Summarizer turns the chosen session's text into a short summary.
	Summarizer Summarizer
	// Disambiguator optionally chooses among multiple plausible sessions when
	// file-overlap scoring is not decisive enough to pick one safely.
	Disambiguator Disambiguator
	// Logf receives best-effort accepted candidate diagnostics. Nil disables logging.
	Logf func(format string, args ...any)
}

ExtractParams configures a single Extract call.

type Match

type Match struct {
	Session *Session
	Score   float64
	// Confidence is the score used to rank accepted candidates after applying
	// recency. Score remains the raw file-overlap score surfaced to callers.
	Confidence float64
	// Overlap lists diff files that appeared in this session. Used purely
	// for diagnostics/telemetry.
	Overlap []string
}

Match is the chosen session along with its overlap score.

type Message

type Message struct {
	Role      Role
	Text      string
	FilePaths []string
	Timestamp time.Time
	// Synthetic marks a message that was inserted by no-slop itself
	// (e.g. a "middle messages omitted" notice from clampMessages). The
	// transcript serializer renders these without a role prefix so the
	// downstream LLM does not mistake them for user or assistant turns.
	Synthetic bool
}

Message is a single user or assistant turn extracted from an agent transcript. Tool calls and tool results are deliberately excluded from Text. FilePaths is a best-effort list of file paths the agent referenced via tool inputs or quoted in assistant text - used purely for matching, not for the summary.

type Reader

type Reader interface {
	Name() string
	// Discover returns candidate sessions matching opts. Implementations must
	// only read enough of each transcript to populate metadata; full message
	// bodies must wait until Load is called.
	Discover(ctx context.Context, opts DiscoverOpts) ([]*Session, error)
	// Load populates s.Messages with user/assistant text only. Tool calls and
	// tool results must be omitted from Message.Text but file paths they
	// reference may be added to Message.FilePaths.
	Load(ctx context.Context, s *Session) error
}

Reader is implemented by per-agent transcript readers.

func AllReaders

func AllReaders(disabled map[string]bool) []Reader

AllReaders returns the default set of agent transcript readers, minus any the caller has disabled by name. Disabled names are matched case-insensitively against each reader's Name().

func NewClaudeReader

func NewClaudeReader() Reader

NewClaudeReader returns a Reader for Claude Code transcripts.

func NewCodexReader

func NewCodexReader() Reader

NewCodexReader returns a Reader for Codex CLI transcripts.

func NewCopilotReader

func NewCopilotReader() Reader

NewCopilotReader returns a Reader for GitHub Copilot CLI transcripts.

func NewOpenCodeReader

func NewOpenCodeReader() Reader

NewOpenCodeReader returns a Reader for OpenCode transcripts.

func NewPiReader

func NewPiReader() Reader

NewPiReader returns a Reader for Pi coding-agent transcripts.

func NewRovoDevReader

func NewRovoDevReader() Reader

NewRovoDevReader returns a Reader for Rovo Dev transcripts.

type Result

type Result struct {
	Summary   string
	AgentName string
	SessionID string
	Score     float64
}

Result is what Extract returns when it successfully attaches an intent to a run. AgentName/SessionID/Score are surfaced for telemetry and DB persistence; callers store the Summary onto db.Run.Intent.

func Extract

func Extract(ctx context.Context, p ExtractParams) (*Result, error)

Extract runs the discover -> match -> optional disambiguate -> cache -> summarize pipeline and returns the final intent. It returns ErrNoMatch when no session satisfies the matcher's threshold, overlap, and freshness acceptance rules. Disambiguation failures fall back to the deterministic match, except cleanup failures are returned because worktree side effects may remain.

type Role

type Role string

Role identifies who produced a transcript message.

const (
	RoleUser      Role = "user"
	RoleAssistant Role = "assistant"
)

type Session

type Session struct {
	AgentName    string
	SessionID    string
	CWD          string
	StartedAt    time.Time
	LastActivity time.Time
	// LastMsgKey is a per-reader stable identifier for the last message
	// (uuid where available, otherwise a timestamp). Used as the cache key
	// input so repeat extractions on an unchanged session hit the cache.
	LastMsgKey string
	// Messages holds the user/assistant turns. Populated by Reader.Load,
	// not by Discover, since most candidates will be filtered out.
	Messages []Message
	// contains filtered or unexported fields
}

Session is one transcript candidate from one agent.

type Summarizer

type Summarizer interface {
	Summarize(ctx context.Context, s *Session) (string, error)
}

Summarizer turns a session's user/assistant text into a 2-6 sentence description of the user's intent.

func NewAgentSummarizer

func NewAgentSummarizer(a agent.Agent, cwd string) Summarizer

NewAgentSummarizer wraps an agent.Agent as a Summarizer. cwd should be the worktree the pipeline will run in.

Jump to

Keyboard shortcuts

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