Documentation
¶
Index ¶
- Constants
- Variables
- func AppendSavedModels(rows ...ModelEntry) error
- func CredentialsPath() (string, error)
- func DeleteCredential(envName string) (bool, error)
- func EntryAPIKey(entry ModelEntry) string
- func EntryDisplay(entry ModelEntry, effort string) string
- func ExpandHome(path string) string
- func IsCanonicalEffort(s string) bool
- func IsStrongProfile(p string) bool
- func LookupKey(envName string) string
- func NormalizeAndValidatePath(workspaceRoot, path string) (string, bool, error)
- func ParseIndexedCommand(cmd string) (kind byte, n int, ok bool)
- func RemoveSavedModel(id string) (bool, error)
- func ResolveNamed(explicit, name string) (string, string, error)
- func ResolvePath(explicit string) (string, string, error)
- func SaveCredential(envName, key string) error
- func SaveWebTools(search, fetch *bool) error
- func SavedKey(envName string) string
- func SavedModelsPath() (string, error)
- func SavedToolsPath() (string, error)
- func SetLinePrompter(fn func(prompt string) (string, bool))
- func SplitModelEffort(ref string) (modelPart, effortPart string)
- func UserDir() (string, error)
- func ValidBackend(b string) bool
- func WireID(display string) string
- func WriteSavedModels(rows []ModelEntry) error
- type AccessType
- type AgentConfig
- type AuditConfig
- type BacktrackConfig
- type BatchToolConfig
- type Config
- func (c *Config) ApplyModel(entry ModelEntry, effort string)
- func (c *Config) ApplyProfile(profile string)
- func (c *Config) CheckPathPermission(path string, accessType AccessType) (PermissionResult, error)
- func (c *Config) CheckPathSafety(toolName, identifier string) error
- func (c *Config) DefaultEffort(entry ModelEntry) string
- func (c *Config) DefaultModelIndex() int
- func (c *Config) DeferredBuiltinTools() map[string]string
- func (c *Config) DeferredMCPServers() map[string]bool
- func (c *Config) EffortOptions(entry ModelEntry) []EffortOption
- func (c *Config) EnabledToolGroups() []ToolGroupConfig
- func (c *Config) FromSavedModels(i int) bool
- func (c *Config) HeadersFor(entry ModelEntry) map[string]string
- func (c *Config) IsStrongFor(entry ModelEntry) bool
- func (c *Config) LLMHeaders() map[string]string
- func (c *Config) ModelDisplay() string
- func (c *Config) ModelList() []ModelEntry
- func (c *Config) ProfileFor(entry ModelEntry) string
- func (c *Config) ReasoningSummaryOrDefault() string
- func (c *Config) ResolveEffort(entry ModelEntry, token string) (string, error)
- func (c *Config) ResolveModel(ref string) (ModelEntry, int, error)
- func (c *Config) ResolveSelection(modelRef, effortRef string, effortSet bool) (int, string, error)
- func (c *Config) SummarizerEntry() (ModelEntry, bool)
- func (c *Config) UseAPIBackend(backend string)
- func (c *Config) WebSearchKeyEnv() string
- func (c *Config) WebSetInConfig() (search, fetch bool)
- func (c *Config) WouldPromptForPath(toolName, identifier string) bool
- type DiagnosticsConfig
- type EditToolConfig
- type EffortOption
- type GitSafetyConfig
- type GlobToolConfig
- type ImagesConfig
- type InterpreterConfig
- type LoadOptions
- type MCPConfig
- type MCPServerConfig
- type MCPToolFilter
- type ModelEntry
- type PDFReadConfig
- type PermissionResult
- type ProcsToolsConfig
- type ProjectInstructionsConfig
- type PromptsConfig
- type QuestionToolConfig
- type ReadToolConfig
- type ReportMode
- type ReportToolConfig
- type RmSafetyConfig
- type SafetyConfig
- type SafetyConfirmation
- type SearchToolConfig
- type ShellToolConfig
- type SubagentToolConfig
- type TerminalTitleConfig
- type ThinkbenchConfig
- type ToolGroupConfig
- type ToolsConfig
- type UIConfig
- type UpdatesConfig
- type WebFetchToolConfig
- type WebSearchToolConfig
- type WebToolsConfig
Constants ¶
const ( DefaultSubagentMaxChildIterations = 50 DefaultSubagentTimeoutS = 300 DefaultSubagentResultMaxChars = 8000 DefaultSubagentMaxConcurrent = 8 )
Defaults for tools.subagent.*. Zero selects the default, never unbounded.
const ( // SavedModelsName holds model rows in the same format as the models: list // of config.yaml. Load appends them after the rows of the config file. SavedModelsName = "models.yaml" // CredentialsName holds API keys as a JSON object from environment // variable name to key, such as {"OPENCODE_API_KEY": "sk-..."}. A row's // api_key_env names the variable; the saved value is used when the // variable is not set. Keeping keys out of the environment matters: every // command the agent's Shell tool runs inherits the environment, so a key // placed there could be printed by any command the model chooses to run. CredentialsName = "credentials.json" // SavedToolsName holds whether Web.search and Web.fetch are on, in the // same form as the tools: block of config.yaml. A setting config.yaml // writes itself takes precedence over the one here. SavedToolsName = "tools.yaml" )
Three files beside the config in ~/.kvit-coder hold what kvit-coder-ui saves when a model or web search is set up from inside it, so that nobody has to edit config.yaml to add a model or export a key in a shell profile (spec/configing.md). All three are read here, by Load, because the agent is a new process every turn and finds its model and tools by reading the configuration again: a row the front end kept only in memory would be refused by the agent. config.yaml itself is only ever written by a person.
const ConfigPathEnv = "KVIT_CODER_CONFIG"
ConfigPathEnv names a config file for every kvit-coder run in the shell that exports it. A -config flag still wins over it.
const DefaultConfigName = "config.yaml"
DefaultConfigName is the file the binaries look for when nobody names one with -config.
const DefaultReportRepairAttempts = 3
DefaultReportRepairAttempts is how many rejected reports a turn may submit before it is told to stop repairing and end with prose instead. Past this the model is spending the turn on the schema rather than on the work.
const DefaultWebSearchKeyEnv = "BRAVE_API_KEY"
DefaultWebSearchKeyEnv is the variable Web.search reads its key from when tools.web.api_key_env names none.
Variables ¶
var CanonicalEfforts = []string{"none", "minimal", "low", "medium", "high", "xhigh", "max"}
CanonicalEfforts are the reasoning-effort values the Responses backend passes through verbatim (cf. grok's ReasoningEffort enum). The config's per-model menu is the authority on what is *selectable*; this set is only what is *spellable*.
var LegacyEfforts = []string{"xhigh", "high", "medium", "low"}
LegacyEfforts is the effort menu for a `responses` model that lists no explicit menu, in presentation order.
Functions ¶
func AppendSavedModels ¶ added in v0.6.2
func AppendSavedModels(rows ...ModelEntry) error
AppendSavedModels adds rows at the end of ~/.kvit-coder/models.yaml. Ids are the caller's to make unique; Load leaves out a saved row whose id is taken.
func CredentialsPath ¶ added in v0.6.2
CredentialsPath is ~/.kvit-coder/credentials.json.
func DeleteCredential ¶ added in v0.6.2
DeleteCredential removes the key saved under an environment variable name and reports whether there was one.
func EntryAPIKey ¶
func EntryAPIKey(entry ModelEntry) string
EntryAPIKey resolves which key an entry authenticates with: the variable its api_key_env names when that is set, else the key saved under the same name in credentials.json, else its literal api_key, else none (a local endpoint needs no key, and the client omits the Authorization header then).
func EntryDisplay ¶
func EntryDisplay(entry ModelEntry, effort string) string
EntryDisplay is ModelDisplay for a catalog row: "model:effort" when an effort is set, plain "model" otherwise.
func ExpandHome ¶
ExpandHome turns a leading ~/ into the user's home directory.
func IsCanonicalEffort ¶
IsCanonicalEffort reports whether s names a known effort value.
func IsStrongProfile ¶
IsStrongProfile reports whether a profile value selects the strong behavior. Empty and unknown values are strong: strong is the default, and only an explicit "weak" (case-insensitive) turns the compensation machinery on. Validation rejects unknown values at load, so unknown here means a Config built without Load (tests, benchmarks constructing one by hand), which should behave exactly like the default.
func LookupKey ¶ added in v0.6.2
LookupKey resolves an api_key_env name: the environment variable when it is set, else the key saved under that name in credentials.json.
func NormalizeAndValidatePath ¶
NormalizeAndValidatePath is a helper function to normalize and validate paths This should be moved to a shared utility package in a real implementation
func ParseIndexedCommand ¶
ParseIndexedCommand parses the word after the colon of :mN / :eN: 'm' or 'e' followed only by digits. Anything else (including bare "m"/"e", which take a value argument) is not an indexed command.
func RemoveSavedModel ¶ added in v0.6.2
RemoveSavedModel deletes the row with this id from ~/.kvit-coder/models.yaml and reports whether there was one.
func ResolveNamed ¶
ResolveNamed is ResolvePath for a config file under some other name, which is what the benchmark modes use when they look for config-<suffix>.yaml.
func ResolvePath ¶
ResolvePath finds the configuration file to load. An explicit path is returned untouched, so a bad -config fails with the name the user typed rather than silently falling back to another file. Otherwise it looks, in order, at $KVIT_CODER_CONFIG, config.yaml in the current directory, ~/.kvit-coder/config.yaml, and config.yaml beside the binary. The last of those is what lets the command run from any directory: a symlink on $PATH resolves to the checkout, and the checkout's own config.yaml is found there.
The second return value is a note for the user about a candidate that was passed over — empty when there is nothing to say.
func SaveCredential ¶ added in v0.6.2
SaveCredential stores key under an environment variable name in ~/.kvit-coder/credentials.json, which is written readable by its owner only.
func SaveWebTools ¶ added in v0.6.2
SaveWebTools records in ~/.kvit-coder/tools.yaml whether Web.search and Web.fetch are on. A nil value leaves that tool as the file has it.
func SavedKey ¶ added in v0.6.2
SavedKey is the key credentials.json holds under an environment variable name, or "" when there is none. The file is read on every call: it is a few hundred bytes, the agent asks once or twice per turn, and reading it fresh means a key saved by one front end is seen by the next turn of another. An unreadable or malformed file reads as empty here; Load reports it.
func SavedModelsPath ¶ added in v0.6.2
SavedModelsPath is ~/.kvit-coder/models.yaml.
func SavedToolsPath ¶ added in v0.6.2
SavedToolsPath is ~/.kvit-coder/tools.yaml.
func SetLinePrompter ¶
SetLinePrompter installs the function used to ask the person at the terminal a question. Passing nil restores reading the terminal directly, which is what a run with no line reader does.
func SplitModelEffort ¶
SplitModelEffort splits a -m/--model value into its model and effort halves: "m3:xhigh" gives ("m3", "xhigh"), "qwen-local:low" gives ("qwen-local", "low"), ":xhigh" gives ("", "xhigh", default model with that effort), and anything without a known effort suffix stays whole — only the last colon counts, and only when the suffix names a canonical effort, so a wire id with colons ("openai:gpt-4") is never cut.
func UserDir ¶ added in v0.6.2
UserDir is ~/.kvit-coder (%USERPROFILE%\.kvit-coder on Windows), where the sessions, the update-check record and the files above live.
func ValidBackend ¶
ValidBackend reports whether b names a known wire protocol (or is empty, which means the chat-completions default). "messages" is Claude's API.
func WireID ¶
WireID strips a ":effort" suffix back off a display string, so a recorded "model:effort" can be compared against a wire id. A suffix that is not a known effort value is left alone (the wire id itself may contain colons).
func WriteSavedModels ¶ added in v0.6.2
func WriteSavedModels(rows []ModelEntry) error
WriteSavedModels replaces ~/.kvit-coder/models.yaml with these rows.
Types ¶
type AccessType ¶
type AccessType int
AccessType defines the type of file access being requested
const ( AccessRead AccessType = iota AccessWrite )
type AgentConfig ¶
type AgentConfig struct {
// Profile says how much the loop should compensate for the model.
//
// "weak" keeps every mechanism that exists to catch a model getting
// confused: backtracking away from a bad tool call, the duplicate-call
// kill switch, the confirm handshake before an edit is applied, scraping
// tool calls out of prose, fuzzy matching and indentation repair, asking
// the model to explain itself after an anomaly, and retrying an empty
// answer. Each of those was written for a model that needed it.
//
// "strong", the default, skips all of it. On a model that does not make
// those mistakes, each mechanism is a tax: a retry that discards good
// work, a handshake that costs two round trips per edit, a fuzzy match
// that silently edits the wrong lines.
//
// This is the default for models without their own `profile:` — see
// ModelEntry.Profile and Config.ProfileFor.
Profile string `yaml:"profile"`
MaxIterations int `yaml:"max_tool_iterations"`
AgentFile string `yaml:"agent_file"`
ProjectInstructions ProjectInstructionsConfig `yaml:"project_instructions"`
}
AgentConfig configures the agent loop and startup instruction sources.
func (AgentConfig) IsStrong ¶
func (a AgentConfig) IsStrong() bool
IsStrong reports whether the loop should skip the machinery that exists to compensate for a weak model. It is the default.
After ApplyModel runs (once per turn, for the selected catalog entry) this reflects the selected model's effective profile — see ProfileFor — so the places that read it follow a :mN switch with no other change.
type AuditConfig ¶
type AuditConfig struct {
Enabled bool `yaml:"enabled"`
LogDir string `yaml:"log_dir"`
RedactSecrets bool `yaml:"redact_secrets"`
RetentionDays int `yaml:"retention_days"`
}
AuditConfig configures safety audit logging
type BacktrackConfig ¶
type BacktrackConfig struct {
Enabled bool `yaml:"enabled"` // Enable backtrack mode (default: true)
MaxRetries int `yaml:"max_retries"` // Max retries at same history point (default: 5)
InjectUserMessage bool `yaml:"inject_user_message"` // On limit reached: backtrack + inject user message instead of error-in-history
}
BacktrackConfig configures the backtrack error handling mode
type BatchToolConfig ¶
type BatchToolConfig struct {
Enabled bool `yaml:"enabled"`
}
BatchToolConfig configures the Batch tool, which runs several independent tool calls in one request.
type Config ¶
type Config struct {
LLM struct {
BaseURL string `yaml:"base_url"`
APIKey string `yaml:"api_key"`
APIKeyEnv string `yaml:"api_key_env"`
Model string `yaml:"model"`
// APIBackend picks the wire protocol: "chat_completions" (default),
// "responses", or "messages" (Claude's API). Some hosted models are
// served only on one protocol and return an error on the others.
APIBackend string `yaml:"api_backend"`
// Headers are extra "Key=Value" request headers sent with every call
// (supports ${VAR}). Needed by endpoints that demand a routing or
// session header of their own.
Headers []string `yaml:"headers"`
// ReasoningEffort asks a reasoning model for more or less thinking.
// Values are the provider's (commonly minimal, low, medium, high,
// xhigh). The "responses" backend sends it as reasoning.effort; the
// chat-completions backend sends it wherever EffortField points.
ReasoningEffort string `yaml:"reasoning_effort"`
// EffortField names which field of a chat-completions request carries
// the effort: "chat_template_kwargs" (the default: a model whose chat
// template reads it, which is how the local servers work) or
// "reasoning_effort" (OpenAI's own top-level field, which hosted
// gateways such as opencode.ai read). An endpoint that wants one
// ignores the other without saying so, so a wrong value here means the
// effort picked with :eN quietly does nothing. The "responses" backend
// ignores this.
EffortField string `yaml:"effort_field"`
// ReasoningSummary asks a reasoning model for readable text describing
// its thinking. Only the "responses" backend sends it; values are the
// provider's (auto, concise, detailed), and "off" suppresses it.
// Empty follows merge_thinking, because thinking that is never
// requested cannot be merged.
ReasoningSummary string `yaml:"reasoning_summary"`
// RequestTimeout bounds one HTTP request to the model, in seconds.
// A request that exceeds it is retried, so this times the retry count
// is how long a turn can stall on an endpoint that never answers.
// Default 600.
RequestTimeout int `yaml:"request_timeout"`
Temperature float32 `yaml:"temperature"`
MaxTokens int `yaml:"max_output_tokens"`
Context int `yaml:"context"` // Max context size for display (0 = don't show)
MergeThinking bool `yaml:"merge_thinking"` // Merge reasoning_content into content (default: false, discard thinking)
Verbose int `yaml:"verbose"` // 0 = off, >0 = show tool output up to N lines
// GenerationStats asks the endpoint for per-request cost and native
// token counts after each answer. It is an OpenRouter endpoint
// (/generation) and returns 404 everywhere else, so it is off by
// default: leaving it on doubles the HTTP requests per iteration.
GenerationStats bool `yaml:"generation_stats"`
BenchmarkCmd string `yaml:"benchmark_cmd"` // External command for benchmarks (use {prompt} placeholder)
} `yaml:"llm"`
Workspace struct {
Root string `yaml:"root"`
Lock bool `yaml:"lock"` // Refuse to start when another agent is working in this directory (default: false)
PathSafetyMode string `yaml:"path_safety_mode"` // "allow", "block", "warn", "ask_once", "ask_always"
AllowOutsideWorkspace bool `yaml:"allow_outside_workspace"`
AllowedPaths []string `yaml:"allowed_paths"`
AllowedReadPaths []string `yaml:"allowed_read_paths"`
DeniedPaths []string `yaml:"denied_paths"`
// DeniedWritePaths may be read and never written, inside the
// workspace or an allowed path as well as outside.
DeniedWritePaths []string `yaml:"denied_write_paths"`
// ReadOnlyRoot makes the workspace readable only: a file in it is
// written only where allowed_paths also names it.
ReadOnlyRoot bool `yaml:"read_only_root"`
} `yaml:"workspace"`
Agent AgentConfig `yaml:"agent"`
Backtrack BacktrackConfig `yaml:"backtrack"`
Tools ToolsConfig `yaml:"tools"`
Prompts PromptsConfig `yaml:"prompts"`
Safety SafetyConfig `yaml:"safety"`
Diagnostics DiagnosticsConfig `yaml:"diagnostics"`
Thinkbench ThinkbenchConfig `yaml:"thinkbench"`
MCP MCPConfig `yaml:"mcp"`
// ToolGroups defines sets of tools whose schemas and documentation reach the
// model only when it asks for them. See ToolGroupConfig.
ToolGroups []ToolGroupConfig `yaml:"tool_groups"`
UI UIConfig `yaml:"ui"`
// Updates controls the release check in kvit-coder-ui: at most one
// GitHub query per interval, a one-line notice when a newer release
// exists, and the :update command that installs it. The headless agent
// never checks.
Updates UpdatesConfig `yaml:"updates"`
// Models is the optional multi-model catalog (docs/model-selection.md).
// Order is stable: :m1 is the first entry, :m2 the second. When absent,
// ModelList synthesizes a one-entry catalog from the llm: block, so old
// configs behave exactly as before.
Models []ModelEntry `yaml:"models"`
// DefaultModel selects the catalog row a fresh session starts on: a
// 1-based index, id, display name or wire model id (see ResolveModel).
// Empty means the row matching llm.model, else the first row.
DefaultModel string `yaml:"default_model"`
// DefaultEffortValue is the effort fallback after an entry's own
// `default: true` row and llm.reasoning_effort.
DefaultEffortValue string `yaml:"default_effort"`
// contains filtered or unexported fields
}
func Load ¶
Load reads a config file, together with the model rows, API keys and web tool switches kvit-coder-ui saved beside it in ~/.kvit-coder (see userfiles.go).
func LoadWith ¶ added in v0.6.2
func LoadWith(path string, opts LoadOptions) (*Config, error)
LoadWith is Load with options; see LoadOptions.
func (*Config) ApplyModel ¶
func (c *Config) ApplyModel(entry ModelEntry, effort string)
ApplyModel makes an entry plus a resolved effort the active endpoint: the `llm:` fields a model switch can change are overwritten, everything else (headers, timeouts, merge_thinking, ...) stays from the `llm:` block. Downstream code (client construction, session records, context accounting) reads cfg.LLM, so it follows the selection with no other change.
It also makes the entry's effective profile (see ProfileFor) the active one, so a :mN switch to a weak model turns the compensation machinery back on for the next turn, and switching back to a strong one turns it off again. The agent is one process per turn, so this runs once per turn in resolveModelSelection; ApplyProfile restores from the pre-profile snapshot, so repeated calls are idempotent.
func (*Config) ApplyProfile ¶
ApplyProfile makes a profile value the active one: it sets Agent.Profile and forces the weak-model machinery off (strong) or restores the file's own settings (weak). It is idempotent — calling it for the same profile twice, or alternating between profiles, always converges — because the weak branch restores from the snapshot taken before any profile ran, never from the current (possibly forced-off) values.
Configs built without Load (tests constructing Config by hand) have no snapshot yet; the first call snapshots the current values as the file's own, so a manual Config behaves like a file that said exactly that.
func (*Config) CheckPathPermission ¶
func (c *Config) CheckPathPermission(path string, accessType AccessType) (PermissionResult, error)
CheckPathPermission validates if a path can be accessed based on workspace config
func (*Config) CheckPathSafety ¶
func (*Config) DefaultEffort ¶
func (c *Config) DefaultEffort(entry ModelEntry) string
DefaultEffort is the effort a fresh selection of this entry runs with: "" when the entry has no menu (a non-reasoning model never takes an effort, so switching to one clears it), else its `default: true` row, else `llm.reasoning_effort`, else `default_effort`, else "" (no `reasoning:` object on the wire).
func (*Config) DefaultModelIndex ¶
DefaultModelIndex is which catalog row a fresh UI (or a headless run with no --model) starts on: `default_model` when it resolves, else the row whose wire id matches `llm.model`, else the first row.
func (*Config) DeferredBuiltinTools ¶
DeferredBuiltinTools returns the built-in tool names claimed by an enabled group, mapped to the group that claimed them. SetupRegistry uses it to hand those tools to their group instead of registering them.
func (*Config) DeferredMCPServers ¶
DeferredMCPServers returns the names of MCP servers that belong to an enabled group. Those are the servers Connect must not dial at startup: the group dials them the first time the model calls one of their tools.
func (*Config) EffortOptions ¶
func (c *Config) EffortOptions(entry ModelEntry) []EffortOption
EffortOptions returns the :eN menu for an entry in config order: its own list, else the legacy four for a `responses` model without one, else empty (a non-reasoning model has no menu and switching to it clears the effort).
func (*Config) EnabledToolGroups ¶
func (c *Config) EnabledToolGroups() []ToolGroupConfig
EnabledToolGroups returns the groups that are switched on, in configuration order. Order is stable so the tools the model is offered stay byte-identical between requests and the server-side prompt cache keeps hitting.
func (*Config) FromSavedModels ¶ added in v0.6.2
FromSavedModels reports whether row i of the model list came from models.yaml rather than from the config file.
func (*Config) HeadersFor ¶ added in v0.6.2
func (c *Config) HeadersFor(entry ModelEntry) map[string]string
HeadersFor is LLMHeaders for an entry other than the active one, such as the summarizer entry that writes session titles: llm.headers with that entry's headers laid over them.
func (*Config) IsStrongFor ¶
func (c *Config) IsStrongFor(entry ModelEntry) bool
IsStrongFor reports whether the effective profile for an entry skips the weak-model compensation machinery.
func (*Config) LLMHeaders ¶
LLMHeaders parses llm.headers, with the active entry's own headers laid over them, into the header map the LLM client wants. ${VAR} references are expanded against the environment so a session or routing header can come from a secret the config does not contain. ${KVIT_RUN_ID} expands to an ID unique to this process unless the environment already sets one.
func (*Config) ModelDisplay ¶
ModelDisplay is the model name as shown in startup banners and :config: "model:effort" when llm.reasoning_effort is set, plain "model" otherwise, so the banner reflects what the Responses backend is actually asked for.
func (*Config) ModelList ¶
func (c *Config) ModelList() []ModelEntry
ModelList returns the catalog: `models:` in file order followed by the rows saved in models.yaml (see mergeSavedModels), or one entry synthesized from the `llm:` block when there are none. It never returns an empty slice, so callers can index [DefaultModelIndex()].
func (*Config) ProfileFor ¶
func (c *Config) ProfileFor(entry ModelEntry) string
ProfileFor returns the effective profile for an entry: its own `profile:` when set, else the global agent.profile default. The result is normalized to lowercase ("strong", "weak", or "" for the default-strong), so callers can compare or display it directly.
func (*Config) ReasoningSummaryOrDefault ¶
ReasoningSummaryOrDefault says what to ask the provider for. Turning on merge_thinking without this asks the model to think and then throws the readable part away, so an unset value follows merge_thinking.
func (*Config) ResolveEffort ¶
func (c *Config) ResolveEffort(entry ModelEntry, token string) (string, error)
ResolveEffort checks an --effort token (or :e value) against one entry's menu. "" always resolves to "" (clear: omit `reasoning:`), which is what a non-reasoning model needs. A value the model does not offer is rejected with the offered list rather than sent to the API.
func (*Config) ResolveModel ¶
func (c *Config) ResolveModel(ref string) (ModelEntry, int, error)
ResolveModel finds a catalog row by 1-based index ("1"), id, display name, or wire model id, in that order. Indexing is stable config-file order; rows are never sorted (prompt-cache determinism).
The :mN spellings the UI uses are accepted too: a leading ':' is dropped and 'm' followed only by digits means that index, so "-m m3" on the command line finds what ":m3" finds in the UI. (A catalog id that is literally "m<N>" is shadowed by the index; ids look like "spark-go", so this costs nothing in practice.)
func (*Config) ResolveSelection ¶
ResolveSelection resolves a startup/flag selection to a catalog index plus a resolved effort. modelRef may carry an inline ":effort" ("m3:xhigh", "qwen-local:low"); an explicit effort (effortSet) wins over the inline one. Empty modelRef means the default row; "" effort means that row's default ("" for a model with no menu). Errors name what is offered, for the UI banner path and the headless --model path alike.
func (*Config) SummarizerEntry ¶
func (c *Config) SummarizerEntry() (ModelEntry, bool)
SummarizerEntry returns the catalog row flagged `summarizer: true` for session-title generation. It reports false when `models:` is absent (a legacy single-model config has nowhere to flag) or when no entry sets the flag, in which case the caller falls back to the prompt's first words. Validation guarantees at most one entry sets it, so the first match is the only match.
func (*Config) UseAPIBackend ¶ added in v0.3.0
UseAPIBackend records a wire protocol chosen on the command line. It wins over the protocol a catalog entry would otherwise select.
func (*Config) WebSearchKeyEnv ¶ added in v0.6.2
WebSearchKeyEnv is the variable Web.search reads its key from. The key is found the way a model's is, with LookupKey: the variable when it is set, else the key kvit-coder-ui saved under that name.
func (*Config) WebSetInConfig ¶ added in v0.6.2
WebSetInConfig reports which of Web.search and Web.fetch the config file turns on or off itself. tools.yaml cannot change those, so kvit-coder-ui shows them as decided there.
func (*Config) WouldPromptForPath ¶ added in v0.5.0
WouldPromptForPath reports whether CheckPathSafety(toolName, identifier) would prompt a person, without performing any I/O. It mirrors CheckPathSafety's decision structure exactly: inside-workspace and pre-permitted paths never prompt, and the warn/block/allow modes never prompt (warn only prints). Only ask_once (unconfirmed) and ask_always return true.
A subagent child has no UI and would wedge the turn waiting for an answer, so every tool site that calls CheckPathSafety first fails closed with a semantic error when the context IsSubagentChild() and this helper is true (spec/subagents-phase2.md section 3). config cannot import tools, so the child flag never reaches this package — the tools side combines the two.
type DiagnosticsConfig ¶
type DiagnosticsConfig struct {
// InterrogateOnAnomaly enables interrogation. Default off.
InterrogateOnAnomaly bool `yaml:"interrogate_on_anomaly"`
// InterrogateTriggers selects which anomaly triggers fire interrogation. Empty = all.
// Valid values: duplicate_call, pending_blocked, alternating_loop, fatal, backtrack.
InterrogateTriggers []string `yaml:"interrogate_triggers"`
// InterrogateIdenticalThreshold is the consecutive-identical-call count that triggers
// interrogation. Default 2.
InterrogateIdenticalThreshold int `yaml:"interrogate_identical_threshold"`
// InterrogateMaxPerTask hard-caps interrogations per task to avoid interrogation loops.
// Default 3.
InterrogateMaxPerTask int `yaml:"interrogate_max_per_task"`
// InterrogateLogDir is where JSONL episode logs are written. Default
// "benchmarks/.kvit-coder-benchmark/interrogations".
InterrogateLogDir string `yaml:"interrogate_log_dir"`
// InterrogateThenInject is reserved for the later recovery variant; no-op in v1.
InterrogateThenInject bool `yaml:"interrogate_then_inject"`
}
DiagnosticsConfig configures anomaly interrogation/logging (Improvement 2). When enabled, the agent loop asks the model to explain "odd" behavior (loops, repeated identical calls, ignored confirm/cancel) and logs the Q&A to a side channel for offline analysis. Observability only: it does not alter recovery.
func (*DiagnosticsConfig) GetInterrogateIdenticalThreshold ¶
func (d *DiagnosticsConfig) GetInterrogateIdenticalThreshold() int
GetInterrogateIdenticalThreshold returns the identical-call threshold, default 2.
func (*DiagnosticsConfig) GetInterrogateLogDir ¶
func (d *DiagnosticsConfig) GetInterrogateLogDir() string
GetInterrogateLogDir returns the log directory, with a default under benchmarks/.
func (*DiagnosticsConfig) GetInterrogateMaxPerTask ¶
func (d *DiagnosticsConfig) GetInterrogateMaxPerTask() int
GetInterrogateMaxPerTask returns the per-task interrogation cap, default 3.
func (*DiagnosticsConfig) TriggerEnabled ¶
func (d *DiagnosticsConfig) TriggerEnabled(trigger string) bool
TriggerEnabled reports whether the given anomaly trigger should fire interrogation. An empty trigger list means all triggers are enabled.
type EditToolConfig ¶
type EditToolConfig struct {
Enabled bool `yaml:"enabled"`
Mode string `yaml:"mode"` // "lines" (default), "searchreplace", or "patch"
MaxFileSizeKB int `yaml:"max_file_size_kb"`
PreviewMode bool `yaml:"preview_mode"` // enables edit.confirm/edit.cancel
ReadBeforeEditMsgs int `yaml:"read_before_edit_msgs"` // require read within N messages before edit (0 = disabled)
PendingConfirmRetries int `yaml:"pending_confirm_retries"` // max retries when LLM ignores confirm/cancel (0 = disabled, default 5)
FuzzyThreshold float64 `yaml:"fuzzy_threshold"` // searchreplace mode: gates ONLY the fuzzy level (0 = fuzzy off). Whitespace-normalization levels still run unless ExactMatchOnly is set.
// ExactMatchOnly forces searchreplace matching to byte-exact (Level 0) only:
// no rstrip, no leading/trailing whitespace normalization, no fuzzy fallback.
// A non-exact search returns a clean no-match instead of being silently
// re-anchored to a normalized (line-start) position. Default off = today's
// cascade behavior for all existing configs.
ExactMatchOnly bool `yaml:"exact_match_only"`
// ExplicitDelete enables the DeleteLines tool and flips lines-mode empty new_text
// semantics: with this on, an Edit replace with new_text="" blanks the addressed
// line(s) (keeps them) instead of deleting, and deletes go through DeleteLines.
// Default off = today's behavior exactly ("" deletes, no DeleteLines tool).
ExplicitDelete bool `yaml:"explicit_delete"`
}
EditToolConfig configures the edit tool
func (*EditToolConfig) GetEditMode ¶
func (e *EditToolConfig) GetEditMode() string
GetEditMode returns the edit mode, defaulting to "lines" for backward compatibility.
type EffortOption ¶
type EffortOption struct {
Value string `yaml:"value"`
Label string `yaml:"label,omitempty"`
Default bool `yaml:"default,omitempty"`
}
EffortOption is one row of a model's :eN menu. Order in the file is presentation order; Default marks the entry's effort default.
func (EffortOption) Display ¶
func (e EffortOption) Display() string
Display returns the menu text: the label when set, else the value.
type GitSafetyConfig ¶
type GitSafetyConfig struct {
BlockPush bool `yaml:"block_push"`
BlockHardReset bool `yaml:"block_hard_reset"`
BlockCheckoutDiscard bool `yaml:"block_checkout_discard"`
BlockStashDrop bool `yaml:"block_stash_drop"`
BlockCleanForce bool `yaml:"block_clean_force"`
WarnBranchForceDelete bool `yaml:"warn_branch_force_delete"`
}
GitSafetyConfig configures git operation safety
type GlobToolConfig ¶ added in v0.4.0
type GlobToolConfig struct {
Enabled bool `yaml:"enabled"`
MaxResults int `yaml:"max_results"` // Paths returned per call (default: 100); above truncates with a flag
}
GlobToolConfig configures the glob tool
type ImagesConfig ¶
type ImagesConfig struct {
Enabled bool `yaml:"enabled"`
MaxSizeMB int `yaml:"max_size_mb"`
MaxDimension int `yaml:"max_dimension"`
}
ImagesConfig configures image ingestion: the ReadImage tool and the -image flag share the caps. The tool is opt-in like every other tool; -image works whenever it is passed, because naming a file is its own authorization.
type InterpreterConfig ¶
type InterpreterConfig struct {
BlockOneLiners bool `yaml:"block_one_liners"`
Allowed []string `yaml:"allowed"`
}
InterpreterConfig configures interpreter one-liner blocking
type LoadOptions ¶ added in v0.6.2
type LoadOptions struct {
// SkipSavedModels leaves out the rows in ~/.kvit-coder/models.yaml. The
// benchmark modes set it, so a run does not depend on the models someone
// set up on the machine it runs on.
SkipSavedModels bool
// SkipSavedTools leaves out ~/.kvit-coder/tools.yaml, for the same
// reason: web search turned on in kvit-coder-ui would change the tools a
// benchmark run offers its model.
SkipSavedTools bool
}
LoadOptions changes what Load reads besides the config file itself.
type MCPConfig ¶
type MCPConfig struct {
// Enabled is the group toggle. When false (default) no servers are dialed.
Enabled bool `yaml:"enabled"`
// StartupTimeout is the per-server connect + tools/list deadline, in seconds
// (default 20). A server that exceeds it is logged and skipped, never fatal.
StartupTimeout int `yaml:"startup_timeout"`
// CallTimeout is the default per-tool-call deadline, in seconds (default 120),
// overridable per server. MCP calls are exempt from the agent loop's blanket
// 15s tool timeout and use this deadline instead.
CallTimeout int `yaml:"call_timeout"`
// Confirm is the default trust policy for MCP tool calls, overridable per
// server: "block" | "ask_once" | "ask_always" | "trust" (default "ask_once").
Confirm string `yaml:"confirm"`
// SanitizeSchemas flattens/strips JSON Schema constructs ($ref, oneOf, allOf,
// ...) that some grammar-constrained tool-calling templates reject. Opt-in so
// faithful schemas are the default.
SanitizeSchemas bool `yaml:"sanitize_schemas"`
// Servers lists the configured MCP servers.
Servers []MCPServerConfig `yaml:"servers"`
}
MCPConfig configures the Model Context Protocol (MCP) client. MCP lets kvit-coder connect to external tool servers (over stdio or HTTP) and surface each server's tools to the model as ordinary agent tools. The section is absent by default, so existing configs are unaffected and the feature is zero-cost when unused. See docs/archive/mcp-plan.md.
func (*MCPConfig) GetCallTimeout ¶
GetCallTimeout returns the default per-call deadline in seconds, default 120.
func (*MCPConfig) GetConfirm ¶
GetConfirm returns the default confirm policy, default "ask_once".
func (*MCPConfig) GetStartupTimeout ¶
GetStartupTimeout returns the per-server connect deadline in seconds, default 20.
type MCPServerConfig ¶
type MCPServerConfig struct {
// Name namespaces the server's tools (mcp.<name>.<tool>); must be unique.
Name string `yaml:"name"`
// Enabled opts this server in. Zero value false = skipped (opt-in).
Enabled bool `yaml:"enabled"`
// Transport selects the wire protocol: "stdio" (default) or "http".
Transport string `yaml:"transport"`
// stdio transport
Command string `yaml:"command"` // executable to spawn
Args []string `yaml:"args"` // arguments
Env []string `yaml:"env"` // extra "KEY=VALUE" entries appended to inherited env
// Cwd is the working directory for the stdio subprocess. Empty defaults to
// the workspace root, so a server that keys off its own working directory
// (e.g. a codebase indexer) targets the project kvit-coder operates on rather
// than wherever kvit-coder happened to be launched. A relative path is
// resolved against the workspace root.
Cwd string `yaml:"cwd"`
// http transport
URL string `yaml:"url"` // streamable-HTTP endpoint
Headers []string `yaml:"headers"` // extra "Key=Value" request headers (supports ${VAR})
// CallTimeout optionally overrides MCPConfig.CallTimeout for this server, in seconds.
CallTimeout int `yaml:"call_timeout"`
// Confirm optionally overrides MCPConfig.Confirm for this server.
Confirm string `yaml:"confirm"`
// Tools optionally filters which of the server's tools are exposed.
Tools MCPToolFilter `yaml:"tools"`
}
MCPServerConfig configures a single MCP server.
func (*MCPServerConfig) GetCallTimeout ¶
func (s *MCPServerConfig) GetCallTimeout(group *MCPConfig) int
GetCallTimeout returns the per-call deadline for this server in seconds, preferring the server override, then the group default, then 120.
func (*MCPServerConfig) GetConfirm ¶
func (s *MCPServerConfig) GetConfirm(group *MCPConfig) string
GetConfirm returns the confirm policy for this server, preferring the server override, then the group default.
func (*MCPServerConfig) GetTransport ¶
func (s *MCPServerConfig) GetTransport() string
GetTransport returns the transport, defaulting to "stdio".
type MCPToolFilter ¶
MCPToolFilter optionally restricts which tools a server exposes. Names are matched against the raw (server-side) tool name. An empty Allow list allows all tools; Deny is applied after Allow.
type ModelEntry ¶
type ModelEntry struct {
ID string `yaml:"id"`
Name string `yaml:"name,omitempty"`
Model string `yaml:"model"`
BaseURL string `yaml:"base_url,omitempty"`
APIBackend string `yaml:"api_backend,omitempty"`
APIKey string `yaml:"api_key,omitempty"`
APIKeyEnv string `yaml:"api_key_env,omitempty"`
Context int `yaml:"context,omitempty"`
Efforts []EffortOption `yaml:"efforts,omitempty"`
// EffortField names which field of a chat-completions request carries
// the reasoning effort for this endpoint: "chat_template_kwargs" (the
// default, for a model whose chat template reads it) or
// "reasoning_effort" (OpenAI's own top-level field, which hosted
// gateways read). Empty keeps the `llm:` block's setting. An endpoint
// that wants one spelling ignores the other silently, so a wrong value
// means the effort picked with :eN does nothing at all. The "responses"
// backend ignores this.
EffortField string `yaml:"effort_field,omitempty"`
// Profile overrides agent.profile for this entry: "strong" (the default)
// or "weak". Empty inherits the global agent.profile. A weak entry keeps
// the compensation machinery (backtracking, the duplicate-call kill
// switch, the edit confirm handshake, fuzzy matching, anomaly
// interrogation, prose scraping, empty-answer retries, interpreter
// one-liner rules); a strong one skips it.
Profile string `yaml:"profile,omitempty"`
// Summarizer marks the entry used to generate session titles: a short
// 3-6 word summary of the first prompt, stored in meta.json. At most
// one entry may set it; when none does (or the call fails) the title
// falls back to the first words of the prompt. It never affects model
// switching — see SummarizerEntry.
Summarizer bool `yaml:"summarizer,omitempty"`
// Headers are extra "Key=Value" request headers for this entry only, in
// the form of llm.headers and with the same ${VAR} expansion. They are
// sent on top of llm.headers, and one with the same name replaces the
// llm.headers value. A gateway that wants headers of its own, such as
// OpenCode's User-Agent and x-opencode-session, can then be described by
// its rows alone, without changing what every other model sends.
Headers []string `yaml:"headers,omitempty"`
}
ModelEntry is one catalog row: everything about an endpoint that switching models can change. Non-endpoint settings (headers, timeouts, merge_thinking, ...) always come from the `llm:` block — except the agent profile, which an entry may override per model (see ProfileFor).
func ReadSavedModels ¶ added in v0.6.2
func ReadSavedModels() ([]ModelEntry, error)
ReadSavedModels returns the rows in ~/.kvit-coder/models.yaml, or none when the file does not exist.
type PDFReadConfig ¶
type PDFReadConfig struct {
// MaxPages caps how many pages one call extracts. Output size usually binds
// first, since a dense page is 4-5KB against a 24KB cap.
MaxPages int `yaml:"max_pages"`
// CacheDir is where the PDF reader keeps its translated code between runs.
// Empty means ~/.kvit-coder/wasm-cache. Losing it costs about 1.8 seconds
// on the next turn that reads a PDF, and it is rebuilt automatically.
CacheDir string `yaml:"cache_dir"`
}
PDFReadConfig configures what happens when Read is given a PDF. There is no enable flag: a PDF handed to Read has to produce something, and the choice is between its text and its object structure.
type PermissionResult ¶
type PermissionResult int
PermissionResult indicates the result of a permission check
const ( PermissionGranted PermissionResult = iota PermissionReadOnly PermissionDenied PermissionPromptRequired )
type ProcsToolsConfig ¶
type ProcsToolsConfig struct {
Enabled bool `yaml:"enabled"`
// KillOnExit stops everything this session started when the turn is
// interrupted. On by default: an interrupt that leaves a dev server
// running is a surprise. Turn it off to keep long-lived services alive
// across interrupts.
KillOnExit *bool `yaml:"kill_on_exit"`
}
ProcsToolsConfig configures the tools for work that outlives a turn's iterations: Shell.start, Shell.output, Shell.status, Shell.list, Shell.kill, Shell.tune, Observe.wait and Observe.add. Ephemeral processes die with the turn; persistent ones survive it.
func (*ProcsToolsConfig) ShouldKillOnExit ¶
func (p *ProcsToolsConfig) ShouldKillOnExit() bool
ShouldKillOnExit reports whether an interrupt stops background processes.
type ProjectInstructionsConfig ¶
type ProjectInstructionsConfig struct {
// Enabled defaults to true when unset, so an existing CLAUDE.md is picked up.
Enabled *bool `yaml:"enabled"`
// Path is resolved relative to the directory kvit-coder was launched from.
// Defaults to "CLAUDE.md".
Path string `yaml:"path"`
}
ProjectInstructionsConfig controls the Claude-Code-style project instruction file loaded for headless -p runs.
func (ProjectInstructionsConfig) IsEnabled ¶
func (p ProjectInstructionsConfig) IsEnabled() bool
IsEnabled reports whether project instructions should be loaded.
func (ProjectInstructionsConfig) PathOrDefault ¶
func (p ProjectInstructionsConfig) PathOrDefault() string
PathOrDefault returns the configured project instructions path.
type PromptsConfig ¶
type PromptsConfig struct {
// UseTemplates enables template-based prompt generation (default: false)
UseTemplates bool `yaml:"use_templates"`
// TemplatesDir overrides embedded templates with filesystem directory
TemplatesDir string `yaml:"templates_dir"`
// HotReload enables template reloading on each request (dev mode)
HotReload bool `yaml:"hot_reload"`
}
PromptsConfig configures prompt template system
type QuestionToolConfig ¶
type QuestionToolConfig struct {
Enabled bool `yaml:"enabled"`
// Timeout is how long a run with no terminal waits for an answer to appear
// in the session inbox, in seconds. Zero, the default, falls back at once,
// so a benchmark or scripted run never hangs on a question. A supervised
// headless run sets it to a few minutes.
Timeout int `yaml:"timeout"`
}
QuestionToolConfig configures the Question tool, which asks the person running kvit-coder something and waits for the answer.
type ReadToolConfig ¶
type ReadToolConfig struct {
Enabled bool `yaml:"enabled"`
MaxFileSizeKB int `yaml:"max_file_size_kb"`
MaxReadSizeKB int `yaml:"max_read_size_kb"`
MaxPartialLines int `yaml:"max_partial_lines"`
ShowLineNumbers *bool `yaml:"show_line_numbers"` // nil = default true, for backward compat
PDF PDFReadConfig `yaml:"pdf"`
}
ReadToolConfig configures the read tool
func (*ReadToolConfig) GetShowLineNumbers ¶
func (r *ReadToolConfig) GetShowLineNumbers() bool
GetShowLineNumbers returns whether line numbers should be shown in Read output. Defaults to true for backward compatibility.
type ReportMode ¶
type ReportMode string
ReportMode says which turns must end with a structured report.
const ( // ReportModeOff never asks for one. ReportModeOff ReportMode = "off" // ReportModeMutating asks for one from a turn that changed a file, ran a // command or started a process. A turn that only read and answered ends // with prose as before. This is the default. ReportModeMutating ReportMode = "mutating" // ReportModeTools asks for one from any turn that used a tool. ReportModeTools ReportMode = "tools" // ReportModeAlways asks for one from every turn. ReportModeAlways ReportMode = "always" )
type ReportToolConfig ¶
type ReportToolConfig struct {
Enabled bool `yaml:"enabled"`
// Mode is which turns must end with one: off, mutating (the default),
// tools, or always.
Mode ReportMode `yaml:"mode"`
// MaxRepairAttempts bounds how many rejected reports one turn may submit.
// Zero selects DefaultReportRepairAttempts.
MaxRepairAttempts int `yaml:"max_repair_attempts"`
// MaxBlocks bounds the block list. Zero selects the package default of 20.
MaxBlocks int `yaml:"max_blocks"`
}
ReportToolConfig configures the Report tool, which ends a turn with a validated structured report instead of free prose. See docs/structured-reports.md.
func (ReportToolConfig) Required ¶
func (c ReportToolConfig) Required(usedTools, mutated bool) bool
Required reports whether a turn with these properties owes a report.
func (ReportToolConfig) ResolvedMode ¶
func (c ReportToolConfig) ResolvedMode() ReportMode
ResolvedMode returns the configured mode, defaulting to mutating. An unrecognized value is treated as the default rather than failing a session, because the cost of a stray report is a card nobody asked for.
type RmSafetyConfig ¶
type RmSafetyConfig struct {
AllowInTemp bool `yaml:"allow_in_temp"`
AllowInWorkspaceCwd bool `yaml:"allow_in_workspace_cwd"`
BlockWorkspaceRoot bool `yaml:"block_workspace_root"`
}
RmSafetyConfig configures rm command safety
type SafetyConfig ¶
type SafetyConfig struct {
StrictMode bool `yaml:"strict_mode"` // Fail-closed on parse errors
ParanoidMode bool `yaml:"paranoid_mode"` // Aggressive restrictions
Audit AuditConfig `yaml:"audit"`
Git GitSafetyConfig `yaml:"git"`
Rm RmSafetyConfig `yaml:"rm"`
Interpreters InterpreterConfig `yaml:"interpreters"`
}
SafetyConfig holds enhanced safety mode configuration
type SafetyConfirmation ¶
type SafetyConfirmation struct {
ToolName string `yaml:"-"`
Path string `yaml:"-"`
Timestamp time.Time `yaml:"-"`
}
SafetyConfirmation tracks user confirmations for path access This is memory-only and does not persist across sessions
type SearchToolConfig ¶
type SearchToolConfig struct {
Enabled bool `yaml:"enabled"`
MaxSnippetResults int `yaml:"max_snippet_results"` // Show full snippets up to this many (default: 20)
MaxCompactResults int `yaml:"max_compact_results"` // Show file:line:char up to this many (default: 100)
}
SearchToolConfig configures the search tool
type ShellToolConfig ¶
type ShellToolConfig struct {
Enabled bool `yaml:"enabled"`
AllowedCommands []string `yaml:"allowed_commands"` // allowlist (empty = allow all)
DisallowedCommands []string `yaml:"disallowed_commands"` // blocklist (checked after allowlist)
// InjectEnv, when non-empty, replaces the child process environment for shell
// commands with os.Environ() plus these "KEY=VALUE" entries. Used by the
// thinkbench harness to point python3/pip at the uv environment without naming
// an outside path in the command string. Empty = inherit parent env (default).
InjectEnv []string `yaml:"-"`
// ExecPrefix, when non-empty, is prepended (as an argv) before "sh -c <command>"
// when running shell commands. Used by the thinkbench harness to wrap agent
// commands in an OS sandbox (e.g. bwrap). Empty = run "sh -c" directly (default).
ExecPrefix []string `yaml:"-"`
// AllowInterpreters, when true, lifts the default block on interpreter
// one-liners (python -c, node -e, perl -e, ...). Set by the thinkbench harness,
// whose tasks are interpreter-driven and sandboxed, and opt-in via benchmark
// configs (e.g. SWE-bench, which runs in a Docker container and needs python -c
// to introspect the codebase). Default false (blocked) for normal use.
AllowInterpreters bool `yaml:"allow_interpreters"`
// AllowWithoutAsking answers yes to every command that would otherwise
// stop and ask, except the few that destroy something unrecoverably
// (dd, mkfs and its family). --yolo sets it. What is refused outright
// stays refused: this answers questions, and a denial was never one.
//
// It matters most with no terminal, where an unanswered question is a
// refusal rather than a pause: without this, a headless --yolo run fails
// on the first curl it needs.
AllowWithoutAsking bool `yaml:"allow_without_asking"`
// DefaultTimeout is how long a shell command may run when the call does
// not ask for a timeout, in seconds. Default 120.
DefaultTimeout int `yaml:"default_timeout"`
// MaxTimeout caps what a call may ask for, in seconds. Default 600.
MaxTimeout int `yaml:"max_timeout"`
}
ShellToolConfig configures the shell tool
type SubagentToolConfig ¶ added in v0.5.0
type SubagentToolConfig struct {
Enabled bool `yaml:"enabled"`
MaxChildIterations int `yaml:"max_child_iterations"`
TimeoutS int `yaml:"timeout_s"`
ResultMaxChars int `yaml:"result_max_chars"`
MaxConcurrent int `yaml:"max_concurrent"`
}
SubagentToolConfig configures the Subagent tool, which runs a child agent loop in-process with its own history and a restricted tool set, returning only the final summary text to the parent. Phase 1 is read-only delegation (research); write-capable delegation (general) follows once the safety story is settled.
func (SubagentToolConfig) ResolvedMaxChildIterations ¶ added in v0.5.0
func (c SubagentToolConfig) ResolvedMaxChildIterations() int
ResolvedMaxChildIterations returns max_child_iterations, defaulting to 50. A child that needs 1000 iterations is a turn wearing a costume.
func (SubagentToolConfig) ResolvedMaxConcurrent ¶ added in v0.5.0
func (c SubagentToolConfig) ResolvedMaxConcurrent() int
ResolvedMaxConcurrent returns max_concurrent, defaulting to 8. The permit is per concurrent Subagent: background children hold one from spawn to collect/kill/exit, and blocking runs hold one while they run.
func (SubagentToolConfig) ResolvedResultMaxChars ¶ added in v0.5.0
func (c SubagentToolConfig) ResolvedResultMaxChars() int
ResolvedResultMaxChars returns result_max_chars, defaulting to 8000.
func (SubagentToolConfig) ResolvedTimeoutS ¶ added in v0.5.0
func (c SubagentToolConfig) ResolvedTimeoutS() int
ResolvedTimeoutS returns timeout_s, defaulting to 300.
type TerminalTitleConfig ¶
type TerminalTitleConfig struct {
// Enabled set to false stops the title being written. Unset means on;
// it is only ever written to a terminal, so there is nothing for a
// piped run to break.
Enabled *bool `yaml:"enabled"`
// Running is the icon shown while a turn is running, Asking the one
// shown while a prompt is waiting for an answer (a Question, a path or
// MCP confirmation, the pause prompt), and Waiting the one shown while
// nothing is running and the composer is open. Unset takes the
// ui.DefaultTitle*Icon defaults.
Running *string `yaml:"running"`
Waiting *string `yaml:"waiting"`
Asking *string `yaml:"asking"`
}
TerminalTitleConfig controls the terminal's window title, which a tabbed terminal such as the one in VS Code also uses as the tab label. kvit-coder-ui sets it to an icon for what is happening followed by the session's title. The agent process it spawns writes it too, but only while a prompt of its own is waiting for an answer and only when it was started by the front end; a headless run leaves the title alone.
The pointers are so that "unset" and "set to empty" are different things: an unset icon takes the default, an icon set to "" means that state shows the session title with no icon at all.
func (TerminalTitleConfig) Icons ¶
func (t TerminalTitleConfig) Icons(defRunning, defWaiting, defAsking string) (string, string, string)
Icons returns the configured icons, falling back to the defaults passed in for whichever of the three the config leaves unset. The defaults come from the caller because internal/ui, which owns them, is a package this one cannot import.
func (TerminalTitleConfig) On ¶
func (t TerminalTitleConfig) On() bool
On reports whether the front end should set the window title.
type ThinkbenchConfig ¶
type ThinkbenchConfig struct {
// SuiteDir overrides the auto-discovered suite directory
// (default: benchmarks/thinkbench/suite, discovered from the repo root).
SuiteDir string `yaml:"suite_dir"`
// TimeoutPerRun is the per-task agent-loop timeout in seconds (default: 600).
TimeoutPerRun int `yaml:"timeout_per_run"`
// UVPython pins the python version for the uv env (default: "3.11").
UVPython string `yaml:"uv_python"`
// Sandbox selects the OS sandbox for agent run_command:
// "auto" (default; bwrap→firejail→none), "bwrap", "firejail", "none", or
// "require" (auto, but hard-fail if no sandbox tool is available).
Sandbox string `yaml:"sandbox"`
// Types restricts which task types run (default: all graded types, excluding observed).
Types []string `yaml:"types"`
// Trials is the default trials-per-task (overridden by -n on the command line).
Trials int `yaml:"trials"`
// PipPackages are installed into the shared uv env at preflight so agents can
// use common test deps without reaching outside the sandbox to find them.
// nil (unset) defaults to ["pytest"]; an explicit empty list installs nothing.
PipPackages []string `yaml:"pip_packages"`
// IncludeObserved runs the ungraded ambiguous-spec tasks too (persist-only).
IncludeObserved bool `yaml:"include_observed"`
}
ThinkbenchConfig configures the thinkbench benchmark family (autonomous coding agent tasks graded by a held-out python grader). See thinkbench-plan.md.
func (*ThinkbenchConfig) GetPipPackages ¶
func (t *ThinkbenchConfig) GetPipPackages() []string
GetPipPackages returns the packages to pre-install into the uv env. An unset (nil) list defaults to ["pytest"]; an explicit empty list installs nothing.
func (*ThinkbenchConfig) GetSandbox ¶
func (t *ThinkbenchConfig) GetSandbox() string
GetSandbox returns the sandbox mode, defaulting to "auto".
func (*ThinkbenchConfig) GetTimeoutPerRun ¶
func (t *ThinkbenchConfig) GetTimeoutPerRun() int
GetTimeoutPerRun returns the per-run timeout in seconds, defaulting to 600.
func (*ThinkbenchConfig) GetUVPython ¶
func (t *ThinkbenchConfig) GetUVPython() string
GetUVPython returns the pinned python version, defaulting to "3.11".
type ToolGroupConfig ¶
type ToolGroupConfig struct {
// Name is the tool the model sees, e.g. "Web.browsing". Must be unique
// across groups and must not collide with a built-in tool.
Name string `yaml:"name"`
// Enabled opts the group in. Zero value false = skipped (opt-in).
Enabled bool `yaml:"enabled"`
// Description is what the model reads when deciding whether to open the
// group, and the only thing a group costs while it is unopened. Write it
// as when to reach for the group rather than as a list of what is in it.
Description string `yaml:"description"`
// Category places the group's documentation in the system prompt:
// "filesystem", "shell", "web", "plan" or "mcp".
// Defaults to "mcp".
Category string `yaml:"category"`
// Instructions is prose returned with the reference, for the things the
// member schemas cannot say. InstructionsFile is read into it at load
// time when set; a relative path resolves against the config file's
// directory, so a missing file fails at startup rather than at the call.
Instructions string `yaml:"instructions"`
InstructionsFile string `yaml:"instructions_file"`
// MCPServers names entries of mcp.servers whose tools become this group's
// members. Those servers are not dialed at startup and their tools are not
// advertised to the model directly.
MCPServers []string `yaml:"mcp_servers"`
// Tools names built-in tools to move into this group. They are registered
// as members instead of as tools of their own, so they stop appearing in
// every request.
Tools []string `yaml:"tools"`
}
ToolGroupConfig configures one tool group.
func (*ToolGroupConfig) GetCategory ¶
func (g *ToolGroupConfig) GetCategory() string
GetCategory returns the system-prompt category, defaulting to "mcp".
type ToolsConfig ¶
type ToolsConfig struct {
Read ReadToolConfig `yaml:"read"`
Images ImagesConfig `yaml:"images"`
Edit EditToolConfig `yaml:"edit"`
Search SearchToolConfig `yaml:"search"`
Glob GlobToolConfig `yaml:"glob"`
Shell ShellToolConfig `yaml:"shell"`
Question QuestionToolConfig `yaml:"question"`
Report ReportToolConfig `yaml:"report"`
Procs ProcsToolsConfig `yaml:"procs"`
Batch BatchToolConfig `yaml:"batch"`
Web WebToolsConfig `yaml:"web"`
Subagent SubagentToolConfig `yaml:"subagent"`
// Safety confirmations (runtime only, not persisted)
SafetyConfirmations map[string]SafetyConfirmation `yaml:"-"`
}
ToolsConfig holds per-tool configuration with explicit enable/disable
type UIConfig ¶
type UIConfig struct {
// Markdown selects when the final answer is styled as markdown:
// "auto" (the default), "always", or "never". Auto styles only on a
// terminal; piped output and --json stay raw markdown. Empty and
// unknown values are auto.
Markdown string `yaml:"markdown"`
// TerminalTitle controls the window title the interactive front end
// keeps up to date while it runs.
TerminalTitle TerminalTitleConfig `yaml:"terminal_title"`
}
UIConfig controls terminal output styling.
type UpdatesConfig ¶ added in v0.4.1
type UpdatesConfig struct {
// Enabled gates the automatic check. Nil (unset) means on, so existing
// configs check daily; false turns the background query off while
// :update --check still works on demand. KVIT_NO_UPDATE_CHECK=1 does
// the same without a config edit.
Enabled *bool `yaml:"enabled"`
// IntervalHours is the minimum time between automatic checks (default
// 24). Zero or negative means the default.
IntervalHours int `yaml:"interval_hours"`
}
UpdatesConfig controls the release check in kvit-coder-ui.
func (UpdatesConfig) AutoCheckEnabled ¶ added in v0.4.1
func (u UpdatesConfig) AutoCheckEnabled() bool
AutoCheckEnabled reports whether the background check may run.
func (UpdatesConfig) Interval ¶ added in v0.4.1
func (u UpdatesConfig) Interval() time.Duration
Interval is the minimum time between automatic checks.
type WebFetchToolConfig ¶
type WebFetchToolConfig struct {
Enabled bool `yaml:"enabled"`
Timeout int `yaml:"timeout"`
MaxBytes int64 `yaml:"max_bytes"`
UserAgent string `yaml:"user_agent"`
}
WebFetchToolConfig configures Web.fetch.
type WebSearchToolConfig ¶
type WebSearchToolConfig struct {
Enabled bool `yaml:"enabled"`
Count int `yaml:"count"`
MaxAttempts int `yaml:"max_attempts"`
Timeout int `yaml:"timeout"`
}
WebSearchToolConfig configures Web.search.
MaxAttempts is a setting rather than a constant because the right value depends on a rate limit this program does not control. Four suits one request per second, where a collision clears in about a second and retrying quietly beats handing the model a failure to reason about; a backend without a per-second limit wants one.
type WebToolsConfig ¶
type WebToolsConfig struct {
APIKeyEnv string `yaml:"api_key_env"`
BaseURL string `yaml:"base_url"`
UsageLog string `yaml:"usage_log"`
Search WebSearchToolConfig `yaml:"search"`
Fetch WebFetchToolConfig `yaml:"fetch"`
}
WebToolsConfig configures the tools that reach the network: Web.search, which queries the Brave Search API, and Web.fetch, which retrieves one page and converts it to markdown. The key is named rather than written here, the way llm.api_key_env does it, because the same key is usually shared with other programs and this file is checked in.