Documentation
¶
Overview ¶
Package gortk compresses shell-command output before it reaches an LLM context window. It is a Go-native take on rtk (the Rust "token killer"), built for embedding inside an agent runtime rather than shelling out to an external binary.
Design principles, in order of priority:
- Lossless-by-default. The generic path only bounds size; it never drops signal. A command without a dedicated filter is always safe to pass through gortk.
- Failure-preserving. Per-command filters drop *known noise* (progress bars, "ok" lines, dependency download chatter) but never the lines an agent needs to act on (failures, errors, file:line locations).
- Honest about loss. Whenever a filter drops or truncates anything, it records it in Truncation so the caller — and the agent — knows the view is partial.
The unit of work is a Command (what ran + what it produced) and the result is a Result (a compressed view + truncation metadata).
Index ¶
- Constants
- func ParseCommandLine(line string) (name string, args []string)
- func Run(ctx context.Context, inv Invocation) (Command, Result, error)
- func RunStream(ctx context.Context, inv Invocation, onLine StreamFunc) (Command, Result, error)
- func StripANSI(s string) string
- func StripControl(s string) string
- type Command
- type Discovery
- type DiscoveryEntry
- type ExecRunner
- type FileSink
- type Filter
- type GoTest
- type GroupSpec
- type Invocation
- type JSONSpec
- type LimitSpec
- type LineSpec
- type LogParser
- type LogSpec
- type MatchSpec
- type OutputRule
- type Record
- type RedactOptions
- type Redactor
- type Registry
- func (r *Registry) Compress(cmd Command) Result
- func (r *Registry) Observe(fn func(Command, Result)) *Registry
- func (r *Registry) Register(f Filter) *Registry
- func (r *Registry) RegisterSpec(s Spec) error
- func (r *Registry) WithCompact() *Registry
- func (r *Registry) WithMaxBytes(n int) *Registry
- func (r *Registry) WithNormalize() *Registry
- func (r *Registry) WithRedaction() *Registry
- func (r *Registry) WithRedactionOptions(opts RedactOptions) (*Registry, error)
- func (r *Registry) WithSink(s Sink) *Registry
- type Result
- type Runner
- type RunnerFunc
- type Session
- type Sink
- type Spec
- type Stats
- type StatsReport
- type Stream
- type StreamEvent
- type StreamFunc
- type StreamRunner
- type Truncation
Constants ¶
const DefaultMaxBytes = 32 * 1024
DefaultMaxBytes bounds the passthrough (and post-filter) output size. It is the last line of defence against a runaway command flooding the context. The value (32 KiB) is a deliberately small per-command cap — gortk output is meant to already be compact, unlike a raw shell capture.
const DefaultMaxCaptureBytes = 2 << 20 // 2 MiB per stream
DefaultMaxCaptureBytes bounds each captured stream in ExecRunner. It matches a common 2 MiB host-executor cap so behavior is consistent if you swap runners.
Variables ¶
This section is empty.
Functions ¶
func ParseCommandLine ¶
ParseCommandLine splits a shell command line into a program name and args, honoring single quotes, double quotes, and backslash escapes. It stops at the first shell control operator (| & ; < > ( )) so only the leading command is returned. It is intentionally small — enough to route to a filter, not a full POSIX shell parser.
func Run ¶
Run is a convenience shortcut: run inv with the default in-process session (ExecRunner + the built-in filters) and return the raw capture plus the compressed view. Equivalent to gortk.DefaultSession().Run, but reuses one shared session instead of recompiling filters on each call.
func RunStream ¶
func RunStream(ctx context.Context, inv Invocation, onLine StreamFunc) (Command, Result, error)
RunStream is the streaming convenience shortcut, mirroring the package-level Run: it streams lines from the default in-process session and compresses the final output. Reuses the shared default session.
func StripANSI ¶
StripANSI removes ALL terminal control sequences from s, including SGR color codes. Use for LLM/text contexts where colors are noise. The one canonical sanitizer — other packages share it rather than hand-rolling their own.
func StripControl ¶
StripControl removes cursor/OSC/charset/DCS/2-char control sequences and bare CR/BEL, but KEEPS SGR color codes (\x1b[…m). Use when forwarding logs to a human terminal: control sequences garble layout, but colors are useful.
Types ¶
type Command ¶
type Command struct {
Name string // argv[0], e.g. "go", "git", "golangci-lint"
Args []string // argv[1:], e.g. ["test", "./..."]
Stdout []byte
Stderr []byte
ExitCode int
}
Command is the input to compression: a finished command invocation and the bytes it produced. Stdout/Stderr are kept separate because most filters care about the distinction (e.g. test runners write results to stdout, diagnostics to stderr).
func CommandFromArgs ¶
CommandFromArgs builds a Command from an argv slice (argv[0] is the program). Use this when you ran the command in exec/argv form, e.g. an exec-style request carrying a command name and its arguments.
func CommandFromLine ¶
CommandFromLine builds a Command from a shell command line (the `sh -c "…"` form). The line is tokenized with shell-like quoting so `git commit -m "a b"` yields Name=git, Args=[commit -m "a b"]. Only the first simple command is inspected — everything up to the first pipe/redirect/operator — which is enough to pick a filter.
type Discovery ¶
type Discovery struct {
// contains filtered or unexported fields
}
Discovery records the commands that fell through to lossless passthrough — i.e. those gortk has no dedicated filter for. It is the data-driven answer to "which filter should I write next?" (rtk's "discover"): rather than porting rtk's whole catalog blind, you learn exactly what your agents actually run that isn't yet compressed. Wire it as an observer:
disc := &gortk.Discovery{}
reg := gortk.Default().Observe(disc.Observe)
...
for _, e := range disc.Top(10) {
fmt.Printf("%4d %s\n", e.Count, e.Command)
}
The zero value is ready to use and safe for concurrent use.
func (*Discovery) Observe ¶
Observe records cmd when its result came from passthrough (no filter matched). Its signature matches Registry.Observe, so it can be passed directly. Results that a real filter produced are ignored — those are already covered.
func (*Discovery) Top ¶
func (d *Discovery) Top(n int) []DiscoveryEntry
Top returns the n most-seen uncompressed commands, highest count first (ties broken by total bytes, then name). n <= 0 returns all of them.
type DiscoveryEntry ¶
type DiscoveryEntry struct {
Command string // e.g. "git rebase"
Count int // times seen with no matching filter
Bytes int64 // total uncompressed bytes these passthroughs carried
}
DiscoveryEntry is one uncompressed command family and how often it was seen.
type ExecRunner ¶
type ExecRunner struct {
// MaxCaptureBytes bounds each of stdout/stderr. 0 uses DefaultMaxCaptureBytes.
MaxCaptureBytes int
}
ExecRunner is the default Runner: it runs commands with os/exec and captures stdout/stderr into bounded buffers. Hosts with a hardened executor (process groups, sandboxing, RPC) should implement Runner over that instead.
func (ExecRunner) Run ¶
func (r ExecRunner) Run(ctx context.Context, inv Invocation) (Command, error)
Run executes inv and returns the captured Command. A non-zero exit status is reported in Command.ExitCode and is NOT a Go error. A Go error is returned only when the process could not run to completion (failed to start, context cancelled, timed out) — the partial Command is still returned alongside it.
func (ExecRunner) RunStream ¶
func (r ExecRunner) RunStream(ctx context.Context, inv Invocation, onLine StreamFunc) (Command, error)
RunStream runs inv, invoking onLine for each line of stdout/stderr as it is produced, and returns the full captured Command when the process exits. Exit semantics match Run: a non-zero exit is reported in Command.ExitCode, not as an error; a Go error means the process could not run to completion.
type FileSink ¶
type FileSink struct {
// Dir is where full-output files are written. Created on first use. Empty =
// os.TempDir()/gortk-tee.
Dir string
}
FileSink persists full command output to files under Dir, returning each file's path as the recovery handle. It is the disk-backed Sink (rtk's tee dir). Use it via Registry.WithSink:
reg := gortk.Default().WithSink(gortk.FileSink{Dir: teeDir})
The zero value writes to os.TempDir()/gortk-tee. FileSink is safe for concurrent use; filenames are unique per call.
type Filter ¶
type Filter interface {
// Name identifies the filter in Result.Filter and in logs.
Name() string
// Match reports whether this filter handles the given command. It is given
// the program name and args so it can key off subcommands
// (e.g. only `go test`, not every `go` invocation).
Match(name string, args []string) bool
// Apply produces the compressed Result. It is only called when Match
// returned true.
Apply(cmd Command) Result
}
Filter compresses the output of one family of commands.
Implementations should be pure (no I/O, no global state) so they are trivial to unit-test against captured fixtures.
type GoTest ¶
type GoTest struct{}
GoTest compresses `go test` output. It prefers the structured `-json` stream (the same structured source a Go test runner consumes) and falls back to scanning plain text. It keeps failures, build errors, and the final summary; it drops per-package "ok" lines and "=== RUN"/"--- PASS" chatter, which dominate the byte count on a green run.
type GroupSpec ¶
type GroupSpec struct {
KeyRegex string `json:"key_regex"` // capture group 1 = the grouping key
Line string `json:"line,omitempty"` // header template; {key} and {count}. Default "{key} ({count})"
Examples int `json:"examples,omitempty"` // keep up to N example lines under each header (indented). 0 = none
}
GroupSpec collapses lines that share a key into one summary line per key — rtk's "grouping" strategy (files by directory, errors by type). It runs after the line stage's drop/keep rules, on whatever survives. A line whose KeyRegex captures group 1 is grouped under that key; a line that doesn't match passes through untouched (so a header or error above a list is preserved).
Example — collapse `find` output by directory:
"group": { "key_regex": "^(.*)/[^/]+$", "line": "{key}/ ({count} files)", "examples": 0 }
type Invocation ¶
type Invocation struct {
Name string
Args []string
Dir string // working directory; "" = current
Env []string // extra KEY=VALUE entries, appended to the process env
Stdin []byte // optional stdin
Timeout time.Duration // 0 = no timeout
}
Invocation describes a command to run — the pure input description, with no notion of compression. A Runner turns it into a Command (raw output).
type JSONSpec ¶
type JSONSpec struct {
ArrayField string `json:"array_field"` // top-level field holding the array
ItemTemplate string `json:"item_template"` // e.g. "{Pos.Filename}:{Pos.Line} {Text}"
SummaryTemplate string `json:"summary_template,omitempty"`
}
JSONSpec flattens a JSON array into one compact line per element.
Templates support two syntaxes, auto-detected: if a template contains "{{" it is a full Go text/template (powerful — conditionals, range, printf), executed against the element (a map[string]any); otherwise it uses the lightweight {dotted.path} placeholder form. SummaryTemplate additionally gets {count} / {{.count}}.
type LimitSpec ¶
type LimitSpec struct {
MaxLines int `json:"max_lines,omitempty"`
Keep string `json:"keep,omitempty"` // "tail"(default)|"head"
}
LimitSpec caps the post-transform size and records the loss.
type LineSpec ¶
type LineSpec struct {
Source string `json:"source,omitempty"` // "stdout"(default)|"stderr"|"both"
StripANSI bool `json:"strip_ansi,omitempty"` // remove ANSI color/escape codes first
TrimSpace bool `json:"trim_space,omitempty"`
DropBlank bool `json:"drop_blank,omitempty"`
DedupAdjacent bool `json:"dedup_adjacent,omitempty"`
DropPrefixes []string `json:"drop_prefixes,omitempty"`
DropRegexps []string `json:"drop_regexps,omitempty"`
KeepRegexps []string `json:"keep_regexps,omitempty"` // whitelist: keep ONLY matching lines (drop rules still apply on top)
// TruncateLinesAt caps each surviving line to N runes (adds "…"). 0 = off.
// Equivalent to rtk's truncate_lines_at; good for tools that emit very long
// single lines (minified diffs, embedded data).
TruncateLinesAt int `json:"truncate_lines_at,omitempty"`
}
LineSpec is a line-oriented transform: drop known-noise lines, keep the rest. Keep rules win over drop rules, so you can drop broadly and rescue specifics.
type LogParser ¶
type LogParser struct {
// contains filtered or unexported fields
}
LogParser is a compiled LogSpec. It is read-only after construction (safe for concurrent use) and parses one line at a time, so it serves both batch compression and streaming consumers.
func (*LogParser) Below ¶
Below reports whether a record falls under the spec's MinLevel and so should be dropped in batch mode. Streaming consumers can ignore this and let their own logger threshold filter.
type LogSpec ¶
type LogSpec struct {
Source string `json:"source,omitempty"` // stdout|stderr|both (default both — logs usually go to stderr)
LineRegex string `json:"line_regex"` // named groups become fields; group "msg" is the message
LevelField string `json:"level_field,omitempty"` // capture holding the raw severity (default "level")
LevelMap map[string]string `json:"level_map,omitempty"` // raw token -> canonical level (debug|info|warn|error|fatal)
DefaultLevel string `json:"default_level,omitempty"` // unmatched lines / unknown severities (default "info")
DemotePatterns []string `json:"demote_patterns,omitempty"` // message matches one -> level becomes "debug"
MinLevel string `json:"min_level,omitempty"` // drop records below this (default: keep all)
Template string `json:"template,omitempty"` // per-record render; {field} or Go template; default "{msg}"
}
LogSpec is the declarative log transform.
type MatchSpec ¶
type MatchSpec struct {
Command string `json:"command,omitempty"` // base program name, e.g. "git"
Subcommands []string `json:"subcommands,omitempty"` // first positional must be one of these
ArgsContain []string `json:"args_contain,omitempty"` // every string must appear in args
// CommandRegex matches against the reconstructed command line
// "<base-name> <arg> <arg> …" (e.g. "uv sync --frozen"). When set, it is the
// sole match condition. Equivalent to rtk's match_command.
CommandRegex string `json:"command_regex,omitempty"`
}
MatchSpec decides which commands a Spec applies to. Use either the structured fields (Command + Subcommands/ArgsContain) or CommandRegex — the regex form mirrors rtk's match_command and is the simplest way to port its filters.
type OutputRule ¶
type OutputRule struct {
Pattern string `json:"pattern"` // regex against the full output blob
Unless string `json:"unless,omitempty"` // if this also matches, skip the rule
Message string `json:"message"` // emitted when Pattern matches and Unless doesn't
}
OutputRule is one whole-blob collapse rule for Spec.MatchOutput.
type Record ¶
type Record struct {
// Level is the canonical severity: debug|info|warn|error|fatal (or "" when
// the line carries no level).
Level string
// Fields are the named captures / parsed values for the line. Always
// includes "msg" (the message) and "level".
Fields map[string]any
// Text is the rendered line (per the spec's template).
Text string
}
Record is one parsed line of structured output: a canonical severity level, the named fields extracted from it, and the rendered text. Produced by log specs and reusable by streaming consumers (see LogParser).
type RedactOptions ¶
type RedactOptions struct {
// Entropy turns on the catch-all high-entropy token scanner. It is more
// aggressive and can mis-fire on opaque-but-harmless tokens, so it is off by
// default; the allowlist (UUIDs, hex hashes) keeps common identifiers safe.
Entropy bool
// MinEntropyLen is the shortest token the entropy scanner considers. 0 -> 24.
MinEntropyLen int
// EntropyThreshold is the minimum Shannon entropy (bits/char) to redact. 0 -> 4.0.
EntropyThreshold float64
// Extra are additional regexes; each whole match is replaced with [REDACTED].
Extra []string
}
RedactOptions tunes the redactor. The zero value (used by WithRedaction) is the high-precision pattern set with no entropy scanning.
type Redactor ¶
type Redactor struct {
// contains filtered or unexported fields
}
Redactor masks credentials in text. Build one with WithRedaction / WithRedactionOptions; it is immutable and safe for concurrent use.
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry holds an ordered set of filters and applies the first one that matches. The zero value is not usable; build one with New.
func Default ¶
func Default() *Registry
Default returns a Registry wired with the built-in filters: the hand-written structured parsers (go test) plus the embedded declarative Specs (golangci-lint, git status). This is the intended entry point for embedding gortk in an agent: keep one Default registry and call Compress on every command's output.
It panics only if the embedded defaults are malformed, which is a build-time guarantee covered by tests.
func New ¶
New builds a Registry with the given filters (first match wins) and the default size bound.
func (*Registry) Compress ¶
Compress runs the first matching filter, then bounds the result size. If no filter matches, it returns a lossless passthrough of stdout+stderr (still size-bounded). Compress never errors and never panics on a well-formed Command — a command with no special handling is simply passed through.
After producing the view it records the savings (InputBytes/OutputBytes), persists the full output through any configured Sink when the result is lossy (Truncation.FullRef), and fans the (cmd, result) pair out to observers.
func (*Registry) Observe ¶
Observe registers a callback invoked with (cmd, result) after every Compress, for metrics and discovery. Multiple observers run in registration order. Observers must not mutate their arguments. Returns the registry for chaining. See Stats (savings) and Discovery (unmatched commands) for ready-made ones.
func (*Registry) Register ¶
Register appends a filter, giving it lowest priority. Use this to add project-specific filters on top of Default().
func (*Registry) RegisterSpec ¶
RegisterSpec compiles a Spec and appends it as a lowest-priority filter.
func (*Registry) WithCompact ¶
WithCompact returns a copy of the registry that applies a final whitespace-collapsing pass to every result (rtk's "-u" ultra-compact spirit): runs of blank lines collapse to one and leading/trailing blanks are trimmed. It only ever removes whitespace, so it is safe to leave on; dropped blank lines are still recorded in Truncation.
func (*Registry) WithMaxBytes ¶
WithMaxBytes returns a copy of the registry with a different size bound.
func (*Registry) WithNormalize ¶
WithNormalize returns a copy of the registry that collapses volatile high-cardinality tokens (UUIDs, ISO timestamps, IPs, hex digests) to stable markers (<uuid>, <ts>, <ip>, <hash>). This cuts tokens and lets dedup collapse otherwise-unique lines. It changes content (loses the specific identifier), so it is opt-in; substitutions are counted in Truncation.Masked.
func (*Registry) WithRedaction ¶
WithRedaction returns a copy of the registry that masks credentials in every result — including lossless passthrough, since unfiltered commands (env, printenv, cat .env) are the likeliest to leak. Uses the high-precision default pattern set (cloud keys, tokens, JWTs, PEM private keys, key=value secrets, URL credentials). Strongly recommended for any output bound for a model. Masked spans are counted in Truncation.Masked.
func (*Registry) WithRedactionOptions ¶
func (r *Registry) WithRedactionOptions(opts RedactOptions) (*Registry, error)
WithRedactionOptions is WithRedaction with control over entropy scanning and extra patterns. It returns an error only if an Extra regex fails to compile.
type Result ¶
type Result struct {
// Text is the compressed output to hand to the model.
Text string
// Filter is the name of the filter that produced this Result, or "passthrough"
// when no dedicated filter matched.
Filter string
// Truncation records what (if anything) was dropped. The zero value means
// nothing was lost — the Text is a complete, faithful view.
Truncation Truncation
// Records carries structured output when a structured stage (a log spec)
// produced it: one entry per surviving line, with its level and parsed
// fields. nil for text-only filters. This is the "structured data out" path
// — a caller can route records to a logger or consume fields directly
// instead of (or alongside) reading Text.
Records []Record
// InputBytes is the size of the raw command output (stdout+stderr) before
// compression; OutputBytes is len(Text) after it. Both are set by
// Registry.Compress so callers can report savings (see SavedBytes /
// SavedFraction and the Stats aggregator). Zero on a hand-built Result that
// never went through a Registry.
InputBytes int
OutputBytes int
}
Result is the compressed view of a Command's output plus a record of what was lost producing it.
func (Result) Lossless ¶
Lossless reports whether the Result preserved everything (nothing dropped or truncated).
func (Result) SavedBytes ¶
SavedBytes reports how many bytes compression removed (InputBytes-OutputBytes, never negative). Meaningful only on a Result returned by Registry.Compress.
func (Result) SavedFraction ¶
SavedFraction reports the fraction of bytes removed, in [0,1]. Returns 0 when there was no input. This is gortk's answer to rtk's "gain" metric.
type Runner ¶
type Runner interface {
Run(ctx context.Context, inv Invocation) (Command, error)
}
Runner executes an Invocation and returns the captured output as a Command.
A Runner MUST NOT compress or interpret output — that is the Registry's job. This is the extension point a host implements over its own executor (a hardened or sandboxed exec RPC, say); ExecRunner is the standalone os/exec implementation used by the CLI.
type RunnerFunc ¶
type RunnerFunc func(context.Context, Invocation) (Command, error)
RunnerFunc adapts an ordinary function to the Runner interface — handy for tests and for wrapping an existing executor without a new type.
func (RunnerFunc) Run ¶
func (f RunnerFunc) Run(ctx context.Context, inv Invocation) (Command, error)
Run implements Runner.
type Session ¶
Session pairs a Runner (input) with a Registry (output). It is the only place the two halves meet, and it is optional sugar: callers who already have raw output just call Registry.Compress directly.
func DefaultSession ¶
func DefaultSession() *Session
DefaultSession is ExecRunner + Default(): run a command and compress its output with the built-in filters, all in process.
func NewSession ¶
NewSession composes a Runner and a Registry.
func (*Session) Run ¶
Run executes the invocation and compresses its output. The returned Command is the raw capture (so callers can keep it); Result is the compressed view. A non-zero exit is reflected in both, not returned as an error.
func (*Session) RunStream ¶
func (s *Session) RunStream(ctx context.Context, inv Invocation, onLine StreamFunc) (Command, Result, error)
RunStream executes the invocation with live line callbacks, then compresses the final output. If the session's Runner does not support streaming, it degrades to a batch Run (onLine is not called) so callers can use one code path regardless of runner capability.
type Sink ¶
type Sink interface {
// Save persists cmd's full output and returns an opaque handle (e.g. a file
// path) the caller can later use to retrieve it. An error means "could not
// save" — Compress then leaves FullRef empty and proceeds; recovery is a
// best-effort convenience, never a hard dependency.
Save(cmd Command, res Result) (ref string, err error)
}
Sink persists the full, uncompressed output of a command so a lossy Result can carry a handle (Truncation.FullRef) back to it. Configure one with Registry.WithSink; FileSink is the built-in disk implementation (rtk's "tee"). A Sink is only consulted when a Result actually dropped something.
type Spec ¶
type Spec struct {
Name string `json:"name"`
Match MatchSpec `json:"match"`
// MatchOutput collapses the whole output to a one-line message when a
// pattern matches the full blob — e.g. a successful `git push` becomes
// "ok (pushed)". Borrowed from rtk's most effective compression stage. The
// first matching rule wins; a rule is skipped if its Unless pattern also
// matches (so error/warning output is never hidden behind an "ok"). Checked
// before JSON/Lines.
MatchOutput []OutputRule `json:"match_output,omitempty"`
JSON *JSONSpec `json:"json,omitempty"`
Lines *LineSpec `json:"lines,omitempty"`
Group *GroupSpec `json:"group,omitempty"` // collapse surviving lines by a captured key
Log *LogSpec `json:"log,omitempty"` // parse a log stream into levelled Records
Limit *LimitSpec `json:"limit,omitempty"`
EmptyText string `json:"empty_text,omitempty"` // text to emit when nothing survives
}
Spec is a declarative filter definition. Exactly one of JSON or Lines should drive the transform; if both are set, JSON is tried first and Lines is the fallback when stdout isn't valid JSON.
func DefaultSpecs ¶
func DefaultSpecs() []Spec
DefaultSpecs returns the built-in specs across all embedded ecosystem files, in a deterministic order (filename, then declaration order within a file).
type Stats ¶
type Stats struct {
// contains filtered or unexported fields
}
Stats accumulates compression savings across many commands — gortk's answer to rtk's "gain" report. Wire it into a Registry as an observer:
stats := &gortk.Stats{}
reg := gortk.Default().Observe(stats.Observe)
...
fmt.Println(stats.Report()) // total saved across every Compress
The zero value is ready to use and safe for concurrent use.
func (*Stats) Observe ¶
Observe records one result's sizes. Its signature matches Registry.Observe, so it can be passed directly.
func (*Stats) Report ¶
func (s *Stats) Report() StatsReport
Report returns a snapshot of the accumulated savings.
type StatsReport ¶
type StatsReport struct {
Commands int
InputBytes int64
OutputBytes int64
SavedBytes int64
SavedFraction float64 // [0,1]
DroppedLines int64
}
StatsReport is an immutable snapshot of cumulative savings.
func (StatsReport) String ¶
func (r StatsReport) String() string
String renders a one-line summary, e.g. "gortk: 12 cmds, 1.2 MiB -> 64 KiB (95% saved)".
type StreamEvent ¶
StreamEvent is one line of output observed while a command runs.
type StreamFunc ¶
type StreamFunc func(StreamEvent)
StreamFunc receives output lines as they are produced. It is called serially (stdout and stderr never overlap), so implementations need no locking.
type StreamRunner ¶
type StreamRunner interface {
Runner
RunStream(ctx context.Context, inv Invocation, onLine StreamFunc) (Command, error)
}
StreamRunner is an optional capability: a Runner that can also stream output line-by-line. ExecRunner implements it. Hosts whose executor can't stream implement only Runner; Session.RunStream degrades gracefully for those.
type Truncation ¶
type Truncation struct {
Happened bool
// DroppedLines counts whole lines removed as known noise.
DroppedLines int
// DroppedBytes counts bytes removed by size bounding.
DroppedBytes int
// Masked counts in-place substitutions made by redaction (secrets) and
// normalization (volatile tokens like UUIDs). Unlike DroppedLines/DroppedBytes
// these don't remove a line — they replace a span with a marker — so they do
// not affect Lossless(): the signal "something was here" is preserved.
Masked int
// Note is a short human/agent-readable explanation, e.g.
// "kept 3 failing tests, dropped 412 passing".
Note string
// FullRef, when non-empty, is a handle to the complete, uncompressed output,
// persisted by the Registry's Sink (see Registry.WithSink). It is the missing
// half of "honest about loss": the loss is not only recorded but recoverable,
// so an agent that needs a line a filter dropped can fetch the full text
// instead of re-running the command. Empty when no Sink is configured or
// nothing was dropped.
FullRef string
}
Truncation describes loss introduced during compression. It is a compact, serializable record so callers can surface a "this view is partial" signal to their agent or UI.