Documentation
¶
Overview ¶
Package config loads mivia TOML configuration and resolves provider settings.
Index ¶
- Constants
- Variables
- func ClearProviderDefaultModel(path, providerName string) error
- func DefaultConfigCandidates() []string
- func DefaultEnvCandidates() []string
- func DefaultStorePathForWorkspace(root string) string
- func EffectiveOutputTokens(profile ModelSpec, requested *int) *int
- func EffectivePromptTokens(profile ModelSpec, maxTokens *int, operatorCap, requested int) int
- func EffectiveTimeoutSec(configured int, overrides ...int) int
- func ExpandPath(p string) string
- func FirstExisting(paths []string) (string, bool)
- func IsAlwaysPolicy(raw string) bool
- func IsAutoPolicy(raw string) bool
- func IsDefaultOrchestrationStorePath(path string) bool
- func IsDenyPolicy(raw string) bool
- func IsInteractiveTTY(f any) bool
- func IsOllamaLoopback(raw string) bool
- func LoadProviderDefaultOverrides(path string) (map[string]string, error)
- func LoadWorkspaceVerifiers(workspaceRoot string) (map[string]VerifierProfile, error)
- func Lookup(key string, file map[string]string) (string, bool)
- func MCPConfigDigest(cfg MCPConfig) (string, error)
- func MemoryBackstopBytes(memoryBackstopMB int) int
- func ModelOffersReasoning(spec ModelSpec) bool
- func ModelReasoning(spec ModelSpec) reasoning.Setting
- func NormalizeApprovalPolicy(raw string) string
- func NormalizeDefaultMode(raw string) string
- func NormalizeModelName(name string) (string, error)
- func ParseTruthyEnv(v string) bool
- func ProjectConfigExists(root string) bool
- func ProjectConfigPath(workspaceRoot string) string
- func PromptAPIKey(stdout io.Writer, stdin io.Reader, provider, keyEnv string) (string, error)
- func RemoveAgentFile(dir string, name string) error
- func RemoveMCPServerConfig(path string, id string) error
- func RemoveProviderConfig(path string, name string) error
- func RequestedTimeoutSec(configured int, explicit int, taskOverrides ...int) int
- func TempStorePath(root, name string) string
- func ToolResultBytesWarnings(tc ToolsConfig) []string
- func UpdateActiveModelConfig(path string, provider, model string) error
- func UpdateChatNoticeConfig(path string, showIteration, showPromptCache bool) error
- func UpdateGeneralConfig(path string, view GeneralSettings) error
- func UpdateMCPServerConfig(path string, srv MCPServerSettings) error
- func UpdateProjectConfig(path string, ps ProjectSettings) error
- func UpdateProviderConfig(path string, pv ProviderSettings) error
- func UpdateProviderDefaultModel(path, providerName, modelName string) error
- func UserAgentsDir() string
- func UserAuthPath() string
- func UserConfigPath() string
- func UserEnvPath() string
- func ValidateHTTPSURL(raw string) (*url.URL, error)
- func WorkspaceAgentsDir(root string) string
- func WriteAgentFile(dir string, ag AgentFileSettings, systemPrompt string) error
- func WriteDefaultUserConfig(path string) error
- func WriteUserEnvKey(path, key, value string) error
- type AgentCollectionState
- type AgentDiscoveryReport
- type AgentFileDiagnostic
- type AgentFileSettings
- type AgentFileSpec
- type AgentFileState
- type AgentSource
- type AgentsGlobal
- type ApprovalsConfig
- type ChatConfig
- type ContextConfig
- type ContextSummaryConfig
- type File
- type GeneralSettings
- type HarnessConfig
- type HookConfigFile
- type HooksSource
- type IntegrationsConfig
- type LoadOptions
- type LoadedAgentFile
- type MCPConfig
- type MCPHeaderConfig
- type MCPServerConfig
- type MCPServerSettings
- type MemoryConfig
- type MessagingConfig
- type MessagingRoutingConfig
- type ModelSettings
- type ModelSpec
- type PrivacyConfig
- type ProjectSettings
- type ProviderConfig
- type ProviderModelGroup
- type ProviderRuntime
- type ProviderSection
- type ProviderSettings
- type Resolved
- func (r *Resolved) AllowsModel(name string) bool
- func (r *Resolved) ModelCatalog() []ProviderModelGroup
- func (r *Resolved) ModelChoices() string
- func (r *Resolved) ModelChoicesFor(providerName string) string
- func (r *Resolved) OtherProvidersWithModel(exclude, name string) []string
- func (r *Resolved) SetModelCatalogForTest(catalog []ProviderModelGroup)
- func (r *Resolved) Validate() error
- type SubagentConfig
- type TUIConfig
- type TaskRetryConfig
- type TavilyConfig
- type ToolsConfig
- type VerifierCommand
- type VerifierProfile
- type WorkflowPanelLimits
- type WorkflowsConfig
- type WorktreeConfig
Constants ¶
const ( ApprovalPolicyWriteOnly = "write-only" ApprovalPolicyAuto = "auto" ApprovalPolicyAlways = "always" ApprovalPolicyDeny = "deny" )
Approval policies. There are exactly three effective states, matching the TUI settings screen's three choices ("once", "always", "deny"):
- ApprovalPolicyWriteOnly ("once"): prompt for every write/external tool call.
- ApprovalPolicyAuto ("always"): auto-approve every tool call, no prompt.
- ApprovalPolicyDeny ("deny"): auto-deny every gated tool call, no prompt.
ApprovalPolicyAlways is kept ONLY as a legacy input alias for the old [approvals] policy = "always" key, which meant "prompt for every call, including reads" (paranoid mode) - a different concept from the settings-screen's "always" ("accept always"/auto-approve). The two vocabularies collide on the bare word "always" with opposite meanings, which is why DefaultMode and Policy are normalized by two different functions below (NormalizeDefaultMode vs NormalizeApprovalPolicy) instead of one shared switch.
const ( MinMemoryEntryBytes = 256 MaxMemoryEntryBytes = 65536 MaxMemorySearchResults = 50 )
memory bounds. Below the entry floor a memory cannot hold its template; above the ceiling a save would dominate the store. max_search_results is capped so one tool call stays a small, bounded read.
const ( MinInspectRepositoryBytes = 4 << 10 MaxInspectRepositoryBytesLimit = 256 << 10 )
Bounds for tools max_inspect_repository_bytes. Below the floor the fixed JSON envelope (provenance + one result) cannot fit; above the ceiling the tool stops being a small, bounded read.
const ( MinTavilyResponseBytes = 1024 MaxTavilyResponseLimit = 64 << 20 )
Tavily response bound limits. Below the floor every legitimate response fails; above the ceiling, budget + input allowance + framing slack risks overflowing the dispatcher's ceiling derivation, which would silently drop the backstop to its floor while the wire read stayed effectively infinite.
const ( // BatchResultBudgetOff disables the mechanism (the default). BatchResultBudgetOff = 0 // BatchResultBudgetDerived derives the budget from the model's prompt // budget instead of naming a number. BatchResultBudgetDerived = -1 // MinBatchResultBudgetBytes is the smallest literal budget that can hold: // it matches the loop's degrade floor, below which the first oversized // result overshoots by construction. MinBatchResultBudgetBytes = 16 << 10 )
Aggregate per-batch tool-result budget knob values (tools batch_result_budget_bytes). The agent loop owns the enforcement; these are the operator-facing constants its config surface is validated against.
const ( DefaultProvider = "openrouter" DeepSeekProModel = "deepseek-v4-pro" )
Built-in provider defaults.
const ( DefaultStreamIdleTimeoutSeconds = 100 DefaultStreamFirstByteTimeoutSeconds = 240 )
DefaultStreamIdleTimeoutSeconds and DefaultStreamFirstByteTimeoutSeconds mirror internal/provider's own watchdog defaults (provider. DefaultStreamIdleTimeout / DefaultStreamFirstByteTimeout). Duplicated rather than imported: internal/provider already imports internal/config (ollama.go, provider.go), so config importing provider back would cycle.
const DefaultAgentName = "mivia"
DefaultAgentName is the root-session agent selected when --agent is omitted and a definition with this name is available.
const DefaultMemoryBackstopMB = 256
DefaultMemoryBackstopMB is the shipped OOM guard when memory_backstop_mb is unset or non-positive (cannot be accidentally disabled via 0).
const DefaultOrchestrationTimeoutSec = 12 * 60 * 60 // 12 hours
DefaultOrchestrationTimeoutSec is the finite parent-tool / batch budget used when default_timeout_seconds is 0 (or omitted). Long enough for multi-step subagent work; never unbounded so cancel/timeout always surfaces.
const DefaultOutputReserveTokens = 32_768
DefaultOutputReserveTokens is the completion allowance assumed when the operator has NOT set [chat] max_tokens.
A model's max_output_tokens is a per-response CEILING, not a typical response size. Using it as the default reserve did two harmful things at once: it asked the provider for that much output on EVERY request, and - because the reserve is subtracted from the context window to derive the prompt budget - it permanently removed that much prompt capacity. On a 200k-window model declaring a 128k ceiling (every Claude and GLM entry in the shipped config) that left only 72k of usable prompt and compacted history at 57.6k, under a third of the window the user believed they had.
The reserve itself is not optional: providers validate input_tokens + max_tokens <= context_window and reject the request outright, so the subtracted reserve and the wire max_tokens must stay in lockstep. Only the DEFAULT value changes here. An operator who genuinely wants long responses sets [chat] max_tokens explicitly, which is authoritative up to the model ceiling.
const DefaultPromptCapTokens = 200_000
DefaultPromptCapTokens is the recommended [chat] max_prompt_tokens value. It bounds the per-request prompt budget for models with large context windows. The planner compacts history at 80% of the budget. It is a recommendation, not a compiled default: an unset knob keeps the window-derived budget.
const DefaultSubagentRequestTimeoutSec = 1800 // 30 minutes
DefaultSubagentRequestTimeoutSec is the per-LLM-request context deadline for a subagent turn when default_request_timeout_seconds is 0 (or omitted). Product decision: 30 minutes. It bounds one provider request, not the whole task. The 15-minute http.Client wire wall (DefaultHTTPTimeout) stays the hard per-attempt bound under it.
const DefaultSubagentTotalTimeoutSec = 3600 // 60 minutes
DefaultSubagentTotalTimeoutSec is the whole-subagent wall-clock budget applied when default_total_timeout_seconds is 0 (or omitted). Product decision: 60 minutes. Unlike the per-request deadline above, this bounds the ENTIRE run - every request, tool call, and wait added together - so a provider that trickles bytes forever still ends inside a finite window. It is the last-resort termination guarantee; a smaller per-task timeout from the caller wins when it is tighter. It must stay comfortably above DefaultSubagentRequestTimeoutSec: the total budget is the outer context every per-request deadline is derived from, and a child context.WithTimeout can never extend past its parent's deadline - a total shorter than the request default would silently truncate a single legitimate call before it ever reached its own documented allowance.
const DefaultTavilyAPIKeyEnv = "TAVILY_API_KEY"
const DefaultUserConfigTOML = `` /* 389-byte string literal not displayed */
DefaultUserConfigTOML is the minimal config content written for a first-time user when no config file exists anywhere. It selects the shipped default provider (openrouter) and its default model, matching the shipped example at .mivia/mivia.toml.example. internal/cli/setup.go (the explicit `mivia setup` command) and loadFile's silent auto-bootstrap path (LoadOptions.AutoBootstrapUserConfig) both write this exact content, so there is one source of truth for "what a brand-new user's config looks like".
It intentionally omits an [approvals] section: config.ApprovalsConfig's zero value already resolves to ApprovalPolicyAuto (accept every tool call, no prompts) via ApprovalsConfig.ApprovalPolicy() - see internal/config/approvals_config.go - so an absent section and an explicit `default_mode = "always"` behave identically. The commented section below only documents that default for a reader of their own generated file, mirroring .mivia/mivia.toml.example's comment.
const DefaultWorktreeBranchPrefix = "mivia/"
DefaultWorktreeBranchPrefix is the prefix for branches that mivia creates when the project config does not set [worktrees].branch_prefix.
const MaxSchemaRetryMax = 10
MaxSchemaRetryMax is the load-time ceiling for [subagents] schema_retry_max. A positive configured value above it is clamped to it, so an operator typo (40 typed instead of 4) cannot configure a 40+-call schema-retry storm, where every retry is a full provider round-trip. Values <= 0 keep the existing "use the default 2" behavior; only 1..MaxSchemaRetryMax pass through.
const MaxTimeoutSeconds = 315_360_000 // 10 years
MaxTimeoutSeconds is the overflow-safety ceiling for every timeout that EffectiveTimeoutSec returns. It is NOT a policy cap: raise-only semantics let a model push any effective timeout up to 10 years, far beyond any real task. The clamp exists so a huge model-supplied timeout_seconds (which parses fine and fits int64) cannot overflow time.Duration when multiplied by time.Second: 10 years = 3.15e17 ns << MaxInt64 (9.22e18), and even dispatchOrchestrationSec's +15s slack stays safe (3.15e8+15 << 9.2e9 s). Without it, a wrapped-negative duration is ignored by the agent loop, which falls back to DefaultToolTimeout (60s) - far below the operator floor.
const RootAgentName = "general-orchestrator"
RootAgentName is the compiled identity of the main (root) session agent. The root surface is never a registry member, so this name must stay reserved: file-backed definitions may not use it, and selecting it (flag, /agent, picker) restores the root surface. Constraint for future built-ins: no compiled built-in may carry this name either - the reservation in checkNameCollisions rejects it for every input, by design.
const UnknownContextWindowTokens = 128_000
UnknownContextWindowTokens is the fail-closed context window tokens (128,000) used when a model's context window is undeclared or unrecognized.
const UsefulToolResultRequestBytes = 4 << 20
UsefulToolResultRequestBytes is a practical upper bound for a single provider-request tool-result carry size. A nonzero max_tool_result_bytes above this is accepted but warned (never clamped).
Variables ¶
var DefaultMemoryConfig = MemoryConfig{
StoreBackend: "sqlite",
MaxEntryBytes: 8192,
MaxEntries: 500,
MaxSearchResults: 8,
}
DefaultMemoryConfig is the resolved default for memory (plan 68).
var DefaultMessagingConfig = MessagingConfig{ MaxBodyBytes: defaultMessagingMaxBodyBytes, MaxMessagesPerTask: defaultMessagingMaxMessagesPerTask, SteerWatchdogSeconds: intPtr(defaultMessagingSteerWatchdogSeconds), MailboxCapacity: defaultMessagingMailboxCapacity, MaxPendingQuestions: defaultMessagingMaxPendingQuestions, Routing: MessagingRoutingConfig{ Mode: defaultMessagingRoutingMode, MaxAsksPerTask: defaultMessagingMaxAsksPerTask, MaxReferralDepth: defaultMessagingMaxReferralDepth, MaxReferralSpawnsPerRun: defaultMessagingMaxReferralSpawns, }, }
DefaultMessagingConfig is the resolved default for [subagents.messaging].
var DefaultSubagentConfig = SubagentConfig{ MaxWorkers: 0, MaxDepth: 0, MaxFanout: 0, DefaultTimeout: 0, DefaultBudget: 0, NestedSteps: 0, SystemPrompt: "", MaxAuditRounds: 0, InlineOutputBytes: defaultInlineOutputBytes, SchemaRetryMax: 2, SpawnStaggerMs: defaultSpawnStaggerMs, Messaging: DefaultMessagingConfig, }
Default subagent config values. All bounds default to 0 (unlimited); users who want caps set them in [subagents] in mivia.toml.
var DefaultToolsConfig = ToolsConfig{ RunTimeoutSec: 900, MaxReadBytes: 0, MaxWriteKB: 0, MaxOutputBytes: 0, MaxListDirEntries: 0, RedactToolArgs: false, MaxTavilyResponseBytes: 4 << 20, MaxFetchKB: 4096, MemoryBackstopMB: 256, MaxInspectRepositoryBytes: 64 << 10, }
DefaultToolsConfig defines the built-in tool policy defaults.
var DefaultWritePathBlocklist = []string{}
DefaultWritePathBlocklist is the built-in set of workspace paths that workflow agent write tools refuse. It ships empty: protection is opt-in via tools write_path_blocklist, not a compiled-in default a project must opt out of. A project that wants .git and .mivia/mivia.toml protected again (recommended - see .mivia/mivia.toml.example) adds them explicitly; tools write_path_blocklist_remove still works against whatever a project adds, in case a future built-in entry is reintroduced.
Functions ¶
func ClearProviderDefaultModel ¶
ClearProviderDefaultModel removes the default_model key from [providers.<providerName>] in the TOML config file at path, leaving the rest of that provider's stanza (if any) intact. If removing the key leaves the stanza empty, the stanza itself is dropped so clearing a project-scope override does not litter the project file with an inert [providers.x] table that sets nothing. A path/provider with no default_model key set is not an error: clearing an override that is already absent is a no-op, the same tolerance a repeated command should have. Locked and atomic (see updateConfigFile).
func DefaultConfigCandidates ¶
func DefaultConfigCandidates() []string
DefaultConfigCandidates returns config paths in search order.
func DefaultEnvCandidates ¶
func DefaultEnvCandidates() []string
DefaultEnvCandidates returns env file paths when env_file is unset.
func DefaultStorePathForWorkspace ¶
DefaultStorePathForWorkspace returns the default SQLite path for root.
func EffectiveOutputTokens ¶
EffectiveOutputTokens returns the response allowance for one request: the completion size asked for on the wire, and the reserve subtracted from the context window to derive the prompt budget. Those two must stay in lockstep - providers validate input_tokens + max_tokens <= context_window - so this is the single place both are decided. A nil result means no ceiling applies.
An EXPLICIT request ([chat] max_tokens) is authoritative up to the model's own ceiling. An UNSET request falls back to the model ceiling capped at max(DefaultOutputReserveTokens, reasoning.OutputReserveFloor(profile.Reasoning)), because a model's max_output_tokens is a per-response maximum rather than a sensible per-request default; see DefaultOutputReserveTokens for the prompt-budget damage the uncapped fallback caused. The reasoning. OutputReserveFloor term matters because the wire request layer (internal/provider's effectiveMaxTokens) applies that SAME floor as its own max_tokens fallback for an unset request - reserving only DefaultOutputReserveTokens here for a high-reasoning-effort model (e.g. z.ai's GLM-5.3 family at "max" effort, floor 65536) would let this budget pack history right up to a boundary the wire request then asks to exceed, risking a prompt_tokens+max_tokens over-context-window rejection this function has every input needed to avoid.
func EffectivePromptTokens ¶
EffectivePromptTokens computes the local prompt budget after reserving the configured completion allowance and applying an optional operator/session cap.
The returned budget is never negative. When the completion reserve consumes or exceeds the whole context window, the budget is 0: the guard fails closed and the provider-side prompt-too-long recovery is the backstop. Validated config cannot reach that state - load rejects context_window_tokens below minContextWindowTokens (1024) and windows at or below the max_tokens reserve - so 0 only surfaces for hand-constructed profiles in tests and runtime bindings.
A non-positive ContextWindowTokens is the undeclared/unrecognized window fallback: it fails closed at UnknownContextWindowTokens (128k). Validated config also never reaches that branch (load rejects windows below 1024); it exists for hand-constructed profiles in tests and legacy runtime bindings.
func EffectiveTimeoutSec ¶
EffectiveTimeoutSec returns a positive timeout in seconds for subagent / orchestration work. configured is DefaultTimeout or a batch/task override; when configured is <= 0, DefaultOrchestrationTimeoutSec is used as the floor so work cannot hang forever. The function is raise-only: overrides can push the effective timeout up, never below the configured floor. A smaller positive override does not shrink the budget; when several are supplied, the largest override bounds the enclosing operation.
EffectiveTimeoutSec is the right helper for fallback budgets (recovery, unset overrides, per-task floors relative to a batch). For explicit caller-requested timeouts that should be honored as the actual budget — not floored to the 12h default — use RequestedTimeoutSec instead.
The result is clamped to MaxTimeoutSeconds. This is an overflow-safety clamp, not a policy cap: raise-only semantics still hold for every value below 10 years, and every downstream time.Duration(n)*time.Second stays positive (see MaxTimeoutSeconds).
func ExpandPath ¶
ExpandPath expands leading ~ to the user home directory.
func FirstExisting ¶
FirstExisting returns the first path that exists as a regular file.
func IsAlwaysPolicy ¶
IsAlwaysPolicy reports whether the policy string represents always-prompt (the legacy "paranoid" policy vocabulary, not the settings-screen "always"/accept-always choice).
func IsAutoPolicy ¶
IsAutoPolicy reports whether the policy string represents auto-approval (YOLO mode).
func IsDefaultOrchestrationStorePath ¶ added in v0.1.1
IsDefaultOrchestrationStorePath reports whether path is the config-layer default orchestration-ledger location (see defaultStorePath): the one tier whose directory no operator manages, so opens may harden it 0600/0700. An operator-configured store_path compares false and keeps its modes.
func IsDenyPolicy ¶
IsDenyPolicy reports whether the policy string represents auto-deny (every gated tool call is rejected without a prompt).
func IsInteractiveTTY ¶
IsInteractiveTTY reports whether f is an *os.File attached to a terminal. Shared helper for the `mivia setup` prompt gate and mivia chat's first-run auto-setup gate so both use the identical check. f is typically an io.Reader (stdin) or io.Writer (stdout); it is checked structurally rather than typed as one or the other so one helper serves both.
func IsOllamaLoopback ¶
IsOllamaLoopback reports whether raw is an absolute http(s) URL whose hostname is a loopback literal (127.0.0.1, ::1, localhost), with no userinfo and no fragment. The hostname is matched as a literal string only — no DNS resolution, no CIDR, no IP normalization — so any other host text fails closed. localhost is trusted as loopback per the locked plan; environments where localhost does not resolve to loopback should use 127.0.0.1. The provider layer complements this literal predicate with a construction-time resolution check and a pinned dial (newLoopbackDialContext) so keyless traffic can only reach a verified loopback address; a localhost that resolves to a non-loopback address fails closed.
func LoadProviderDefaultOverrides ¶
LoadProviderDefaultOverrides reads path's own [providers.<name>] default_model keys, unmerged with any other config layer, and returns them as a provider-name-to-model map. It exists so a caller can tell which file actually SETS a given provider's default apart from Load()'s merged/effective result (loadFile's workspace overlay folds a project file's default_model into the same in-memory ProviderConfig the base file populated - see workspace_overlay_test.go - so the merged Resolved alone cannot say whether a provider's effective default came from the user file or a project override). An empty or missing path, or one with no [providers] table, returns an empty map and no error: this is a display-only read, not config validation, and must not block the settings screen from opening over a malformed or absent project file the way Load()'s closed-shape decode would. Providers with no default_model key are simply absent from the returned map rather than mapped to "". This is a read, not covered by updateConfigFile's write lock, but readConfigMap's os.ReadFile call is itself atomic with respect to any concurrent writeFileAtomic rename (a reader either sees the file entirely before or entirely after a rename, never a partial write).
func LoadWorkspaceVerifiers ¶
func LoadWorkspaceVerifiers(workspaceRoot string) (map[string]VerifierProfile, error)
LoadWorkspaceVerifiers parses ONLY the [verifiers] tables of one workspace's own .mivia/mivia.toml. It is the single source of declared profiles for every surface: config.Load populates Resolved.Verifiers from it (never from a user-level base layer), and `mivia workflows validate` calls it directly, so validation and the run resolve the same catalogue on every machine. A missing config file means no declared profiles, not an error.
func Lookup ¶
Lookup returns the value for key, preferring the process environment over the file map. A blank process environment value falls through to the file; an unset process environment returns the file value; a blank file value falls back to a set-but-empty process environment value. The double os.LookupEnv call keeps the historical contract: a key set to "" in the process environment and absent from file resolves to ("", true), so callers can distinguish unset from set-but-blank when that distinction matters.
func MCPConfigDigest ¶
MCPConfigDigest returns a stable, secret-free digest of MCP configuration.
func MemoryBackstopBytes ¶
MemoryBackstopBytes converts a memory backstop in megabytes to bytes.
func ModelOffersReasoning ¶
ModelOffersReasoning reports whether this model declares any reasoning effort. It is the one predicate every surface asks - config validation, the /model annotation, and the /effort picker's empty state - so "offers nothing" cannot mean different things in different places.
func ModelReasoning ¶
ModelReasoning projects a model profile's reasoning pair as one value, so the request paths that carry it thread a single field instead of two that can drift apart.
func NormalizeApprovalPolicy ¶
NormalizeApprovalPolicy returns the normalized approval policy ("write-only", "auto", "always", or "deny") for the legacy [approvals] policy field and the --approval-policy CLI flag, both of which speak the write-only/auto/always vocabulary. The default fallback for an unset value is ApprovalPolicyAuto: a fresh mivia.toml with no [approvals] section accepts all tools by default rather than prompting.
func NormalizeDefaultMode ¶
NormalizeDefaultMode returns the normalized approval policy for the TUI settings screen's "approval default" vocabulary ("once" | "always" | "deny", config key [approvals] default_mode). Unlike NormalizeApprovalPolicy, "always" here means "accept always" (auto approve) - it normalizes to ApprovalPolicyAuto, not ApprovalPolicyAlways. The default fallback for an unset value is ApprovalPolicyAuto, matching the shipped default of accepting all tools out of the box.
func NormalizeModelName ¶
NormalizeModelName canonicalizes a model identifier accepted from config, flags, slash commands, or persisted sessions. The error deliberately omits the supplied value because model identifiers reach terminal output.
func ParseTruthyEnv ¶ added in v0.1.2
ParseTruthyEnv reports whether an environment value names truth: 1, true, yes, on, y, t (case-insensitive). Anything else is false. Exported for the launcher's MIVIA_MOUSE override; internal resolvers use it too.
func ProjectConfigExists ¶ added in v0.1.1
ProjectConfigExists reports whether root has its own project-scoped config file at <root>/.mivia/mivia.toml, without loading it - a narrower question than Load()'s own `found`, which is also satisfied by the shared user-level ~/.mivia/mivia.toml. Used to decide, at storage-path-resolution time, whether root is a real mivia project (safe to default a durable per-project store under root) or an ad-hoc directory (fall back to config.TempStorePath instead).
func ProjectConfigPath ¶
ProjectConfigPath returns workspaceRoot's own .mivia/mivia.toml path without checking the filesystem, mirroring UserConfigPath's shape for the project layer. Empty workspaceRoot returns "" - there is no project-scoped config to address without a workspace. Callers writing a project-scoped override must additionally check this path differs from whatever base config path is in play (see loadFile's own workspaceOverlayConfigPath guard): when they are the same file, "the project layer" is not a distinct target and treating it as one would silently read a value back as its own override.
func PromptAPIKey ¶
PromptAPIKey prompts for a provider's API key on stdout/stdin with the input masked (golang.org/x/term), mirroring the exact prompt `mivia setup` has always used. Callers must confirm stdin/stdout are both a TTY (see IsInteractiveTTY) before calling this - it does not check itself, so calling it against non-interactive stdin will block waiting for input. Returns the trimmed key, which may be empty if the user enters nothing.
func RemoveAgentFile ¶
RemoveAgentFile deletes an agent markdown file from the given directory. Locked against a concurrent WriteAgentFile to the same path (see lockPersistFile) so a remove cannot interleave with an in-flight write.
func RemoveMCPServerConfig ¶
RemoveMCPServerConfig deletes an MCP server entry from mivia.toml. Locked and atomic (see updateConfigFile).
func RemoveProviderConfig ¶
RemoveProviderConfig removes a provider definition from mivia.toml. Locked and atomic (see updateConfigFile).
func RequestedTimeoutSec ¶
RequestedTimeoutSec returns the timeout budget when the caller provides an explicit timeout_seconds value. Unlike EffectiveTimeoutSec's raise-only floor, an explicit positive value IS the budget — it is not floored to the configured default or DefaultOrchestrationTimeoutSec. This lets the root orchestrator bound a dispatch_tasks batch or delegate call to a shorter window than the global default, which is the intended semantics of the timeout_seconds parameter on orchestration tools.
When explicit is 0 or negative ("use the default"), the configured default applies via EffectiveTimeoutSec, preserving backward compatibility. taskOverrides may still raise the budget above the explicit value: a task may legitimately need more than the batch budget, and the whole-call budget must accommodate the longest task. The result is clamped to MaxTimeoutSeconds.
func TempStorePath ¶ added in v0.1.1
TempStorePath returns the OS-temp-dir path for an ad-hoc (no project config found) store named name, hash-keyed by root via the existing sanitizePath helper (see DefaultStorePathForWorkspace) so distinct ad-hoc roots never collide. Rooted at os.TempDir(), not os.UserCacheDir() like DefaultStorePathForWorkspace: an ad-hoc run names no real project to key a durable, indefinitely-retained cache entry against, so normal OS temp cleanup can reclaim it instead of silently accumulating forever under the user's real home/cache.
func ToolResultBytesWarnings ¶
func ToolResultBytesWarnings(tc ToolsConfig) []string
ToolResultBytesWarnings returns non-fatal operator warnings for the tools result-cap surface. Values are never clamped here — only advised.
func UpdateActiveModelConfig ¶
UpdateActiveModelConfig updates the active provider and default model in mivia.toml. Locked and atomic (see updateConfigFile).
func UpdateChatNoticeConfig ¶
UpdateChatNoticeConfig updates or sets the show_iteration_notices and show_prompt_cache_notices keys under [chat] in the TOML config file at path. Locked and atomic (see updateConfigFile in persist_lock.go) so a concurrent edit to the same file from another goroutine cannot silently lose either write - this mutator previously used its own bespoke read-marshal-write with no synchronization or atomic rename at all, unlike every other config mutator in this package.
func UpdateGeneralConfig ¶
func UpdateGeneralConfig(path string, view GeneralSettings) error
UpdateGeneralConfig persists general and TUI options into the TOML configuration file at path. Locked and atomic (see updateConfigFile in persist_lock.go) so a concurrent edit to the same file from another goroutine cannot silently lose either write and a crash mid-write cannot corrupt the file.
func UpdateMCPServerConfig ¶
func UpdateMCPServerConfig(path string, srv MCPServerSettings) error
UpdateMCPServerConfig inserts or updates an MCP server entry in mivia.toml. Locked and atomic (see updateConfigFile).
func UpdateProjectConfig ¶
func UpdateProjectConfig(path string, ps ProjectSettings) error
UpdateProjectConfig persists project settings into the TOML configuration file at path. Locked and atomic (see updateConfigFile).
func UpdateProviderConfig ¶
func UpdateProviderConfig(path string, pv ProviderSettings) error
UpdateProviderConfig adds or updates a provider and its models in mivia.toml. Locked and atomic (see updateConfigFile).
func UpdateProviderDefaultModel ¶
UpdateProviderDefaultModel updates or sets the default_model key under [providers.<providerName>] in the TOML config file at path. If the provider section doesn't exist yet, it will be initialized with [providers.<providerName>]. Locked and atomic (see updateConfigFile in persist_lock.go) so a concurrent default-model edit to the same file from another goroutine cannot silently lose either write.
func UserAgentsDir ¶
func UserAgentsDir() string
UserAgentsDir returns ~/.mivia/agents without checking the filesystem.
func UserAuthPath ¶
func UserAuthPath() string
UserAuthPath returns the path to the local CLI auth token file (~/.mivia/auth.json) without checking the filesystem.
func UserConfigPath ¶
func UserConfigPath() string
UserConfigPath returns the fixed user-level config path without checking the filesystem.
func UserEnvPath ¶
func UserEnvPath() string
UserEnvPath returns the fixed user-level env path without checking the filesystem.
func ValidateHTTPSURL ¶
ValidateHTTPSURL rejects anything but a well-formed absolute https URL. It carries no per-provider or per-environment relaxation; a caller needing a loopback or env-var exception applies it before/after calling this.
func WorkspaceAgentsDir ¶
WorkspaceAgentsDir returns <root>/.agents/agents without checking the filesystem.
func WriteAgentFile ¶
func WriteAgentFile(dir string, ag AgentFileSettings, systemPrompt string) error
WriteAgentFile writes an agent definition markdown file with YAML frontmatter. Locked per target path (see lockPersistFile) and written atomically via writeFileAtomic so a concurrent edit to the SAME agent file from another goroutine cannot interleave with this write, and a crash mid-write cannot leave a truncated agent file behind.
func WriteDefaultUserConfig ¶
WriteDefaultUserConfig writes DefaultUserConfigTOML to path, creating the parent directory (0700) if needed. It does not check whether path already exists - callers that must not overwrite an existing file need to Stat it themselves first (see internal/cli/setup.go's writeSetupConfigIfMissing and this file's own autoBootstrapUserConfig).
func WriteUserEnvKey ¶
WriteUserEnvKey writes key=value into the env file at path, preserving any existing keys, atomically and with 0600 permissions so the key never appears in a world-readable file. Shared by `mivia setup` and the mivia-chat first-run key prompt (internal/clichat) so there is one place that knows how a key gets persisted to disk.
Types ¶
type AgentCollectionState ¶
type AgentCollectionState string
AgentCollectionState distinguishes a missing agent namespace from an existing namespace with no definition files.
const ( AgentCollectionNotPresent AgentCollectionState = "not present" AgentCollectionEmpty AgentCollectionState = "empty" AgentCollectionHasEntries AgentCollectionState = "has entries" )
type AgentDiscoveryReport ¶
type AgentDiscoveryReport struct {
Files []LoadedAgentFile
Diagnostics []AgentFileDiagnostic
Warnings []string
Collection AgentCollectionState
UserDirectory string
WorkspaceDir string
}
AgentDiscoveryReport contains every safely loaded file and every independent file-level issue. A bad file does not hide valid files from inspection.
func DiscoverAgentFilesReport ¶
func DiscoverAgentFilesReport(workspaceRoot string, loadWorkspace bool) (AgentDiscoveryReport, error)
DiscoverAgentFilesReport discovers both user and workspace definitions while collecting independent malformed, unreadable, and shadowed rows. Workspace agent files are always candidates; loadWorkspace is retained for compatibility and does not gate this collection.
type AgentFileDiagnostic ¶
type AgentFileDiagnostic struct {
Name string
Source AgentSource
Path string
State AgentFileState
}
AgentFileDiagnostic is a bounded, non-secret inspection row. Error details are intentionally reduced to a class; callers must not print parser text.
type AgentFileSettings ¶
type AgentFileSettings struct {
Name string
Description string
Provider string
Model string
Tools []string
Skills []string
MCPServers []string
}
AgentFileSettings contains agent definition for markdown file persistence.
type AgentFileSpec ¶
type AgentFileSpec struct {
Name *string
Description *string
Inherits *string
Tools *[]string
// AllowEmptyTools permits an explicitly declared empty tools list. It is
// valid only for a standalone agent with tools = [].
AllowEmptyTools *bool
ToolsAdd *[]string
ToolsRemove *[]string
DisallowedTools *[]string
// ToolsCore overrides [tools] core for this agent (plan tools/05).
// nil = inherit (parent's decision, else the global [tools] core).
ToolsCore *[]string
// Skills is the skill invocation allowlist for this agent (plan 06).
// nil = omit (root: all trusted skills; inherited: parent decision);
// non-nil empty = none; non-nil with names = those skills only.
Skills *[]string
// MCPServers is the MCP server allowlist. nil inherits the root default or
// parent list. An explicit empty list denies every MCP server.
MCPServers *[]string
// Provider is the built-in provider name owning Model. It is normalized to
// lower case at parse time and may only be set together with Model: a
// provider alone would silently pair a foreign endpoint with the session's
// model name. Provider selection on a workspace definition is ignored
// unless the operator enables AllowWorkspaceAgentProviders (credential-
// routing protection); user definitions always honor it.
Provider *string
Model *string
MaxTurns *int
// TimeoutSeconds and MaxTokens are per-agent resource ceilings, deliberately
// independent of MaxTurns: max_turns = 0 means unlimited iterations, not
// unlimited wall-clock time or provider spend. nil = inherit the session's.
TimeoutSeconds *int
MaxTokens *int
SystemPrompt *string
// OutputSchema is an optional JSON Schema for the agent's final reply
// (plan tools/02). Pointer preserves omit vs empty for inheritance.
OutputSchema *map[string]any
// InputSchema optionally validates task input at admission.
InputSchema *map[string]any
}
AgentFileSpec is one presence-preserving agent TOML definition. Pointer fields distinguish omitted keys from empty values.
func ParseAgentFileMarkdown ¶
func ParseAgentFileMarkdown(data []byte, filename string) (AgentFileSpec, string, error)
ParseAgentFileMarkdown parses a single Markdown agent definition with YAML frontmatter and assigns the Markdown body to SystemPrompt.
func ParseAgentFileTOML ¶
func ParseAgentFileTOML(data []byte, filename string) (AgentFileSpec, string, error)
ParseAgentFileTOML parses a single agent definition body with unknown-key rejection and presence-preserving optional fields. filename is the base name used for name agreement (e.g. "researcher.toml").
type AgentFileState ¶
type AgentFileState string
AgentFileState describes the safe inspection state of one discovered file.
const ( // AgentFileLoaded means the file parsed and is available for resolution. AgentFileLoaded AgentFileState = "loaded" // AgentFileShadowed means a workspace file lost to a user-name match. AgentFileShadowed AgentFileState = "shadowed by user" // AgentFileMalformed means the file was readable but did not satisfy the // agent-file format or safety contract. AgentFileMalformed AgentFileState = "malformed" // AgentFileUnreadable means the file or containing directory could not be // safely inspected. AgentFileUnreadable AgentFileState = "unreadable" )
type AgentSource ¶
type AgentSource string
AgentSource identifies the trust origin of a loaded agent definition file.
const ( // AgentSourceUser is a trusted definition under ~/.mivia/agents/. AgentSourceUser AgentSource = "user" // AgentSourceWorkspace is an untrusted, gated definition under // <workspace>/.agents/agents/. AgentSourceWorkspace AgentSource = "workspace" // AgentSourceBuiltIn is a compiled definition shipped inside the mivia // binary. Built-ins are product content, not workspace input: they load // regardless of the load_workspace_config gate and always follow the // session provider binding. AgentSourceBuiltIn AgentSource = "builtin" )
type AgentsGlobal ¶
type AgentsGlobal struct {
// LoadWorkspaceConfig enables workspace agent files and related
// workspace-controlled prompt/skill surfaces. Default true.
LoadWorkspaceConfig bool
// AllowWorkspaceAgentProviders, when true, lets a workspace-sourced agent
// definition select a (provider, model) binding. This is an operator opt-in
// that accepts a real credential-routing risk: a checked-out repository
// could route the operator's prompts, tool results, and file contents to
// another vendor's endpoint authenticated with the operator's own
// credentials. When false (the default), a workspace agent's
// provider/model selection is ignored at resolve time (credential-routing
// protection) and the agent inherits the session provider. Only the user
// [agents] section may set this; workspace [agents] is never authoritative.
AllowWorkspaceAgentProviders bool
// RequireExplicitTools, when true, forces authored agents that omit tools
// to resolve an empty allowlist (deny-by-default). Default false.
RequireExplicitTools bool
// FailOnEmptyToolset refuses an agent whose effective toolset is empty.
// Default true so a typo cannot publish a no-tool agent silently.
FailOnEmptyToolset bool
// MandatoryToolDenylistAdditions are operator additions on top of the
// compiled mandatory denylist. Config may only add; never remove baseline.
MandatoryToolDenylistAdditions []string
// Warnings are non-fatal diagnostics (e.g. workspace [agents] ignored).
Warnings []string
// Path is the user config file that supplied these values (may be empty).
Path string
}
AgentsGlobal is the trusted [agents] section of ~/.mivia/mivia.toml. Only the user file owns these values; workspace [agents] is ignored.
func LoadAgentsGlobal ¶
func LoadAgentsGlobal(workspaceRoot string) (AgentsGlobal, error)
LoadAgentsGlobal reads the trusted user config for [agents] gate and guardrails. Workspace [agents] values are never authoritative; when a workspace config path is supplied and contains [agents], a warning is added.
The user file is always UserConfigPath(). Missing user config yields defaults (gate on, fail_on_empty_toolset true).
type ApprovalsConfig ¶
type ApprovalsConfig struct {
Policy string `toml:"policy"`
DefaultMode string `toml:"default_mode"`
}
ApprovalsConfig controls tool execution approval policies. DefaultMode is the single source of truth for the TUI settings screen ("once" | "always" | "deny") and is what session construction resolves into Session.ApprovalPolicy. Policy is kept only as a legacy input alias for DefaultMode's write-only/auto/always/deny vocabulary; when both are set, DefaultMode wins.
func (ApprovalsConfig) ApprovalPolicy ¶
func (a ApprovalsConfig) ApprovalPolicy() string
ApprovalPolicy returns the normalized approval policy ("write-only", "auto", "always", or "deny"), preferring DefaultMode (settings-screen vocabulary) over the legacy Policy field (write-only/auto/always vocabulary). An unset config (both fields empty) resolves to ApprovalPolicyAuto: accept all tools by default - this is the ONE place that default lives; NormalizeApprovalPolicy/NormalizeDefaultMode keep their own conservative (write-only) empty-string fallback because they are also used for already-running sessions (e.g. Session.ToggleYOLO's zero-value field), where "unset" must not silently mean "auto".
func (ApprovalsConfig) IsAuto ¶
func (a ApprovalsConfig) IsAuto() bool
IsAuto reports whether the approval policy is auto (YOLO mode).
type ChatConfig ¶
type ChatConfig struct {
SystemPrompt string `toml:"system_prompt"`
MaxPromptTokens *int `toml:"max_prompt_tokens"`
// MaxContextTokens is retained only as a decode sentinel so the removed
// setting cannot silently change the prompt safety budget.
MaxContextTokens *int `toml:"max_context_tokens"`
Temperature *float64 `toml:"temperature"`
MaxTokens *int `toml:"max_tokens"`
// MaxSteps bounds one interactive turn's agent loop. Unset uses the
// built-in default; 0 means unlimited, which lets a model stuck emitting
// tool calls run until the user interrupts it. /steps overrides per session.
MaxSteps *int `toml:"max_steps"`
// ShowIterationNotices controls whether per-step/iteration notices (e.g. "iteration 1")
// are emitted in the TUI chat. Default: false (disabled).
ShowIterationNotices *bool `toml:"show_iteration_notices"`
// ShowPromptCacheNotices controls whether prompt cache hit/usage notices
// are emitted in the TUI chat. Default: false (disabled).
ShowPromptCacheNotices *bool `toml:"show_prompt_cache_notices"`
}
ChatConfig holds chat session defaults.
type ContextConfig ¶
type ContextConfig struct {
// MaxSourceEventBytes bounds one projected message's payload.
MaxSourceEventBytes int `toml:"max_source_event_bytes"`
// MaxCheckpointBytes bounds a checkpoint's serialized active context.
MaxCheckpointBytes int `toml:"max_checkpoint_bytes"`
// MaxCommitEvents bounds how many messages one turn may publish.
MaxCommitEvents int `toml:"max_commit_events"`
// MaxCommitEventBytes bounds one turn's aggregate payload bytes.
MaxCommitEventBytes int `toml:"max_commit_event_bytes"`
// MaxSessionStateBytes bounds a stored session's serialized messages.
MaxSessionStateBytes int `toml:"max_session_state_bytes"`
// MaxExportBytes bounds a context export.
MaxExportBytes int `toml:"max_export_bytes"`
// SummaryMetadataBytes bounds the persisted summary envelope. Zero (uncapped
// by default) means the host imposes no compiled-in ceiling on
// model-generated summary content.
SummaryMetadataBytes int `toml:"summary_metadata_bytes"`
// CheckpointMetadataBytes bounds the summary_metadata column within a
// checkpoint record. Zero means uncapped.
CheckpointMetadataBytes int `toml:"checkpoint_metadata_bytes"`
// Summary is the [context.summary] policy sub-table. Unlike the numeric
// ceilings above, it is behavior policy, not a storage bound.
Summary ContextSummaryConfig `toml:"summary"`
}
ContextConfig is the operator's ceiling on durable context storage.
Every field is bytes-or-count, and EVERY ONE DEFAULTS TO 0 = UNCAPPED. These used to be constants compiled into the durable contract, sized far below the 200k-1M token windows this product ships against, and because publication is one transaction, exceeding one refused the whole turn: the conversation stopped persisting the first time the agent read a file, and never recovered because an active context only grows. A ceiling here is a deliberate storage decision by someone who knows their disk, never a default that destroys work the agent already finished.
type ContextSummaryConfig ¶
type ContextSummaryConfig struct {
// Enabled turns on the bounded provider call that summarizes what
// compaction dropped. Nil means unset, which resolves to true - the
// pointer exists precisely so an explicit `enabled = false` is
// distinguishable from an absent key, which a plain bool cannot express.
// The call still requires a resolved provider endpoint; without one the
// summary stays off and summaryDisabledReason names why.
Enabled *bool `toml:"enabled"`
// Provider and Model override the binding the summary call runs on,
// allowing summaries to use a cheaper model than the session binding.
// Both keys must be set together, and Provider must be a known provider
// declared under [providers] - the model is not required to appear in
// that provider's declared models (agent-file precedent: a bad model
// degrades at call time, and summary failures are intentionally soft).
// Absent means the summary uses the session binding captured at setup.
Provider *string `toml:"provider"`
Model *string `toml:"model"`
}
ContextSummaryConfig is the operator switch for LLM-backed compaction summaries. It is ENABLED by default: compaction drops messages permanently, so the summary is the only record of what was removed, and a workspace that configures nothing should not silently lose that record. Opting out is explicit.
func (ContextSummaryConfig) SummaryEnabled ¶
func (c ContextSummaryConfig) SummaryEnabled() bool
SummaryEnabled reports the resolved switch: absent means on. Read this rather than the pointer so the opt-out default lives in one place.
type File ¶
type File struct {
EnvFile string `toml:"env_file"`
Provider ProviderSection `toml:"provider"`
Providers map[string]ProviderConfig `toml:"providers"`
Chat ChatConfig `toml:"chat"`
Subagents SubagentConfig `toml:"subagents"`
Worktrees WorktreeConfig `toml:"worktrees"`
Tools ToolsConfig `toml:"tools"`
Privacy PrivacyConfig `toml:"privacy"`
Context ContextConfig `toml:"context"`
Integrations IntegrationsConfig `toml:"integrations"`
MCP MCPConfig `toml:"mcp"`
Memory MemoryConfig `toml:"memory"`
Harness HarnessConfig `toml:"harness"`
Approvals ApprovalsConfig `toml:"approvals"`
TUI TUIConfig `toml:"tui"`
Workflows WorkflowsConfig `toml:"workflows"`
// Verifiers is populated by LoadWorkspaceVerifiers from the WORKSPACE'S
// own .mivia/mivia.toml only (loadFile), never by the tolerant struct
// decode and never from a user-level base layer: a verifier table with an
// unknown key must fail the load, and the profiles that judge a project's
// gates must come from that project's file alone.
Verifiers map[string]VerifierProfile `toml:"-"`
}
File is the on-disk TOML shape (no secrets).
type GeneralSettings ¶
type GeneralSettings struct {
Theme string
Mouse bool
ShowReasoning bool
ShowIterationNotices bool
ShowPromptCacheNotices bool
ScrollLines int
ApprovalDefault string
ScreenReader bool
ReducedMotion bool
}
GeneralSettings contains general and TUI settings for config updates.
type HarnessConfig ¶
type HarnessConfig struct {
// Sandbox controls whether the harness runs verifier/evidence-gate
// commands inside a bubblewrap sandbox (Linux only). nil (the key
// omitted) means enabled, so existing configs load unchanged. Disabling
// it runs those commands directly on the host with no filesystem,
// network, or environment isolation from the workflow - see
// .agents/rules/10-security-privacy.md before turning this off.
Sandbox *bool `toml:"sandbox"`
}
HarnessConfig controls harness-level execution behavior. It is distinct from verifier/project configuration: a project's evidence gates decide WHAT runs, HarnessConfig decides HOW the harness runs it.
func (HarnessConfig) SandboxEnabled ¶
func (h HarnessConfig) SandboxEnabled() bool
SandboxEnabled reports whether the sandbox is enabled (nil means enabled).
type HookConfigFile ¶
type HookConfigFile struct {
// Path is the file these bytes were read from.
Path string
// Data is the raw TOML. Parsing happens in internal/hooks; this type owns
// provenance only.
Data []byte
// Project marks the workspace's own config, as opposed to the user's. Every
// surface that shows a hook shows this, because "which of these came with
// the repository" is the question a reader actually has.
Project bool
}
HookConfigFile is one file's hook bytes and where they came from.
type HooksSource ¶
type HooksSource struct {
// Files are the hook-bearing configs, user config FIRST.
//
// Order is load-bearing rather than cosmetic: PreToolUse stops at the first
// deny, so the user's own gates answer before a repository's do.
Files []HookConfigFile
// Warnings are user-visible startup diagnostics - one per config file that
// declared hooks mivia will not load. A silently ignored hook is how
// someone concludes hooks are broken.
Warnings []string
}
HooksSource is every config file lifecycle hooks may come from.
There are two, and they ADD rather than replace: the user config at its fixed path, and the workspace's own .mivia/mivia.toml. Hooks are the one setting mivia merges across layers, and they have to be - a project's formatter and a user's global gate are not competing answers to one question, they are two hooks, and letting the workspace file replace the user's would silently disarm a gate by opening a repository.
Reading hooks from the workspace means a cloned repository can execute commands on first launch. That is a deliberate product decision, not an oversight: project-defined hooks are the point of the feature. It is stated here, in the `/hooks` listing, at startup and in the docs, because the one thing it must never be is a surprise. Cloning a repository is taking delivery of code you are about to run.
$MIVIA_CONFIG still does not supply hooks. It names the GENERAL config, and a table in it is reported rather than loaded - the workspace file is the project surface, and a second one selected by an environment variable would make "which files can run commands here" depend on how mivia was launched.
func LoadHooksSource ¶
func LoadHooksSource(workspaceRoot string) (HooksSource, error)
LoadHooksSource resolves every config file lifecycle hooks may come from and reports the ones that declared hooks and were not loaded.
A missing user config is not an error: hooks are optional. An unreadable or link-shaped user config IS an error - that file is the operator's own, and an ambiguous read of it fails closed rather than loading zero hooks silently.
func (HooksSource) UserPath ¶
func (s HooksSource) UserPath() string
UserPath is the fixed user config path, for messages that name it.
type IntegrationsConfig ¶
type IntegrationsConfig struct {
Tavily TavilyConfig `toml:"tavily"`
}
IntegrationsConfig holds API keys and config for third-party services.
type LoadOptions ¶
type LoadOptions struct {
ConfigPath string
// WorkspaceRoot selects the project MCP configuration. Empty uses the
// current working directory for backward compatibility.
WorkspaceRoot string
ProviderOverride string
ModelOverride string
AllowMissingConfig bool
// AutoBootstrapUserConfig silently writes a minimal default config to
// UserConfigPath() (DefaultUserConfigTOML) when the normal search
// (opts.ConfigPath, then DefaultConfigCandidates()) finds no config file
// anywhere. It only fires when opts.ConfigPath was left empty - an
// explicit --config/$MIVIA_CONFIG miss stays a real error (or, for
// $MIVIA_CONFIG, falls through to the remaining candidates, unchanged
// pre-existing behavior) rather than being silently papered over. It
// does not require AllowMissingConfig to also be set, but callers should
// normally set both: if HOME cannot be resolved (UserConfigPath() is
// ""), bootstrap is a no-op and AllowMissingConfig alone then decides
// whether that is a hard error or a found=false load.
//
// Defaults to false (zero value) for every existing caller; wire it to
// true only where a config-file-writing side effect on a missing config
// is actually wanted (currently: `mivia chat` only - see
// internal/clichat/chat_command.go's runChat). Every read-only/internal/
// test caller of config.Load keeps today's found=false behavior
// unchanged.
AutoBootstrapUserConfig bool
}
LoadOptions controls config resolution.
type LoadedAgentFile ¶
type LoadedAgentFile struct {
// Name is the canonical agent name from the filename (without .toml).
Name string
Source AgentSource
Path string
Spec AgentFileSpec
}
LoadedAgentFile is one safely-read agent definition with provenance.
func DiscoverAgentFiles ¶
func DiscoverAgentFiles(workspaceRoot string, loadWorkspace bool) ([]LoadedAgentFile, []string, error)
DiscoverAgentFiles loads user agent files and workspace agent files.
Project agent definitions under <ws>/.mivia/agents/ always load when present: they replace the former ungated .mivia/agent-prompt.md surface. The user load_workspace_config gate still controls workspace mivia.toml system prompts and project skill handlers at the CLI layer - not agent file discovery.
loadWorkspace is retained for call-site compatibility and is ignored. Same-directory home/workspace is treated as user only. Workspace files that share a name with a user agent are ignored with a warning. Fail-closed on symlinks, non-regular files, hardlink ambiguity, path escapes, and replacement races.
func DiscoverAgentFilesTolerant ¶
func DiscoverAgentFilesTolerant(workspaceRoot string, loadWorkspace bool) ([]LoadedAgentFile, []string, error)
DiscoverAgentFilesTolerant loads user agent files strictly and workspace agent files tolerantly: a single malformed, symlinked, hardlinked, or oversized file under <ws>/.mivia/agents/ must never abort chat startup (INV-AG-34). The trusted user boundary stays fail-closed - any problem in ~/.mivia/agents/ is still a hard error.
loadWorkspace is retained for call-site compatibility and is ignored. Same-directory home/workspace is treated as user only, exactly like DiscoverAgentFiles; it never routes through the tolerant workspace path. Workspace files that share a name with a user agent are ignored with the same shadow warning as DiscoverAgentFiles. Every other non-loaded workspace file becomes a class-only skip warning; raw parser text is never forwarded (see tolerantSkipWarnings and the agents_diagnostics.go contract).
type MCPConfig ¶
type MCPConfig struct {
Enabled bool `toml:"enabled"`
StartupTimeoutSeconds int `toml:"startup_timeout_seconds"`
MaxServers int `toml:"max_servers"`
MaxToolsPerServer int `toml:"max_tools_per_server"`
MaxToolSchemaBytes int `toml:"max_tool_schema_bytes"`
MaxToolDescriptionBytes int `toml:"max_tool_description_bytes"`
MaxToolResultBytes int `toml:"max_tool_result_bytes"`
Servers []MCPServerConfig `toml:"servers"`
}
MCPConfig controls trusted MCP server definitions. A project definition replaces a user definition with the same server ID as one complete unit.
type MCPHeaderConfig ¶
MCPHeaderConfig maps an HTTP header to the name of its environment value.
type MCPServerConfig ¶
type MCPServerConfig struct {
ID string `toml:"id"`
Transport string `toml:"transport"`
Command string `toml:"command"`
URL string `toml:"url"`
Args []string `toml:"args"`
Env []string `toml:"env"`
Headers []MCPHeaderConfig `toml:"headers"`
Global bool `toml:"global"`
TimeoutSeconds int `toml:"timeout_seconds"`
}
MCPServerConfig is one MCP server definition. It stores only environment variable names. It never stores a secret value.
func LoadScopeMCPServers ¶
func LoadScopeMCPServers(userPath, projectPath string) (userServers, projectServers []MCPServerConfig, err error)
LoadScopeMCPServers loads user and project MCP server configurations separately without merging.
type MCPServerSettings ¶
type MCPServerSettings struct {
ID string
Transport string
Command string
Args []string
Endpoint string
EnvNames []string
}
MCPServerSettings contains MCP server definition for config updates.
type MemoryConfig ¶
type MemoryConfig struct {
// Enabled controls whether the memory tools are wired. nil (the key
// omitted) means enabled, so existing configs load unchanged.
Enabled *bool `toml:"enabled"`
// StoreBackend is "memory" (ephemeral, in-process) or "sqlite"
// (durable, default). Mirrors [subagents] store_backend.
StoreBackend string `toml:"store_backend"`
// StorePath is the project memory database file. Empty resolves by the
// three-tier rule in resolveMemoryConfig: a workspace with its own
// project config defaults to <workspace>/.mivia/memory.db; an ad-hoc
// directory defaults to a temp-dir store keyed by the sanitized root.
// A repo owner may point it at a tracked path and commit memories with
// the repository. Relative paths resolve against the workspace root;
// "~/..." expands to the home directory.
StorePath string `toml:"store_path"`
// OrgID is the org identity for org-scoped memory, honored from the
// user config file only. Empty means org scope is unavailable.
OrgID string `toml:"org_id"`
// MaxEntryBytes caps one rendered entry. Default 8192.
MaxEntryBytes int `toml:"max_entry_bytes"`
// MaxEntries caps the row count per store file. Default 500.
MaxEntries int `toml:"max_entries"`
// MaxSearchResults caps memory_search results. Default 8.
MaxSearchResults int `toml:"max_search_results"`
// BlockPatterns are regexes; a save whose content matches any of them is
// refused. Configuration-only, like the privacy redaction patterns.
BlockPatterns []string `toml:"block_patterns"`
// InjectCore enables auto-injecting the bounded "core" memory tier into
// the system prompt at session start (D1, plan 76). Default false: it
// changes every session's prompt composition, and round-2 review of
// plan 76 found operators who allowlist the mivia binary in
// [tools].run_allowlist weaken D1a's promotion gate - shipping this off
// by default means an operator opts into that exposure per repo,
// consciously, rather than getting new prompt content on upgrade.
InjectCore bool `toml:"inject_core"`
}
MemoryConfig configures durable agent memory (plan 68).
Org identity is USER-owned: org_id is honored only from the user config file (~/.mivia/mivia.toml). A workspace config is repo-controlled and must not name the org store its agents write into, so a workspace org_id is ignored at load (see resolveMemoryConfig).
func (MemoryConfig) IsEnabled ¶
func (m MemoryConfig) IsEnabled() bool
IsEnabled reports whether memory is enabled (nil means enabled).
type MessagingConfig ¶
type MessagingConfig struct {
// Enabled is ignored: messaging is always enabled. Retained so older
// configs with enabled=true|false still parse without error.
Enabled *bool `toml:"enabled"`
// MaxBodyBytes is the per-message inline body budget. Default 2048.
MaxBodyBytes int `toml:"max_body_bytes"`
// MaxMessagesPerTask is the child upstream send quota per attempt. Default 32.
MaxMessagesPerTask int `toml:"max_messages_per_task"`
// MailboxCapacity is parent→child mailbox depth (phase 03). Default 32.
MailboxCapacity int `toml:"mailbox_capacity"`
// MaxPendingQuestions is RESERVED and a no-op: the effective value is always
// 1. Exactly one park per task is structurally enforced by the question
// registry (one pendingQuestion per runID/taskID key) plus the awaiting_input
// single-bit ledger status; N>1 is unsupported. The field still parses from
// TOML (and the config resolver still fills the default of 1) so existing
// configs load unchanged, but nothing reads it for behavior.
MaxPendingQuestions int `toml:"max_pending_questions"`
// SteerWatchdogSeconds: nil = default (300s); explicit 0 = disabled
// (unbounded); positive = seconds.
SteerWatchdogSeconds *int `toml:"steer_watchdog_seconds"`
// Routing is parent-side Ask referral policy (plan 53.04). Always active.
Routing MessagingRoutingConfig `toml:"routing"`
}
MessagingConfig is the [subagents.messaging] surface for typed, budgeted agent messages. Messaging is always on; Enabled is accepted in TOML for forward compatibility but ignored (IsEnabled always returns true).
func (MessagingConfig) IsEnabled ¶
func (m MessagingConfig) IsEnabled() bool
IsEnabled always returns true. Messaging cannot be disabled (product decision 2026-08-03); the TOML enabled field is ignored if present.
func (MessagingConfig) SteerWatchdogSecondsResolved ¶
func (m MessagingConfig) SteerWatchdogSecondsResolved() int
SteerWatchdogSecondsResolved returns the effective steer watchdog interval in seconds: nil → the default 300, an explicit 0 → disabled (unbounded), otherwise the configured value. Single source of truth for the CLI handler construction sites (plan 54 §4.5); the config-layer resolver (resolveMessagingConfig) fills nil the same way, so this is idempotent on both resolved and raw configs.
type MessagingRoutingConfig ¶
type MessagingRoutingConfig struct {
// Mode is "policy" (default) or "parent" (unimplemented).
Mode string `toml:"mode"`
// MaxAsksPerTask bounds UNANSWERED asks posted by one task. Default 4.
// Semantics: the slot is released when an ask is answered or sealed.
MaxAsksPerTask int `toml:"max_asks_per_task"`
// MaxReferralDepth is max hops in an ask chain (A→B→C = 2). Default 2.
MaxReferralDepth int `toml:"max_referral_depth"`
// Allow is "from_role->to_role" pairs. Empty = any live same-run role;
// referral-as-spawn always requires an explicit pair.
Allow []string `toml:"allow"`
// MaxReferralSpawnsPerRun caps referral-as-spawn. Default 4.
MaxReferralSpawnsPerRun int `toml:"max_referral_spawns_per_run"`
}
MessagingRoutingConfig is [subagents.messaging.routing] for peer referral. mode "policy" is implemented; "parent" is declared but unimplemented.
type ModelSettings ¶
type ModelSettings struct {
Name string
ContextWindowTokens int
MaxOutputTokens int
Reasoning string
ReasoningEfforts []string
}
ModelSettings contains model information for config updates.
type ModelSpec ¶
type ModelSpec struct {
Name string `toml:"name"`
ContextWindowTokens int `toml:"context_window_tokens"`
MaxOutputTokens int `toml:"max_output_tokens,omitempty"`
// ReasoningEfforts is the ordered set of reasoning levels this model
// offers. Empty means the model has no reasoning surface. Order is
// preserved because it is the order the /effort picker lists.
ReasoningEfforts []reasoning.Level `toml:"reasoning_efforts,omitempty"`
// Reasoning is this model's DEFAULT effort and must be a member of
// ReasoningEfforts. Empty means the model ships with no reasoning field
// sent, which a model that offers efforts may still choose - the user
// opts in through /effort. Reasoning belongs to the model rather than to
// [chat] because capabilities and value sets differ per model, so one
// session-global value would be wrong for every model it did not match.
Reasoning reasoning.Level `toml:"reasoning,omitempty"`
// ReasoningDialect is this model's wire shape. Empty uses the provider's
// vetted default where one exists; load refuses an active level with no
// resolvable dialect rather than letting the key silently do nothing.
ReasoningDialect reasoning.Dialect `toml:"reasoning_dialect,omitempty"`
}
ModelSpec is one explicitly configured provider model and its physical context capacity. The name is provider-qualified by its containing group.
func ResolveModelProfile ¶
ResolveModelProfile finds the profile named name in profiles. If absent, it synthesizes a profile with UnknownContextWindowTokens and reports found=false.
func (*ModelSpec) UnmarshalTOML ¶
UnmarshalTOML enforces the narrow model object shape. A scalar model array is rejected instead of being silently treated as an empty catalog.
It implements go-toml's unstable.Unmarshaler, which is unversioned by name and whose signature has already changed across releases (*unstable.Node in v2.2.3, []byte later). Dispatch is a runtime type assertion, so a dependency bump that changes it does not break the build: this method would simply stop being called, every check below would become dead code, and the closed model shape would silently reopen. Any go-toml upgrade must re-run the model shape tests, not just `go build`.
type PrivacyConfig ¶
type PrivacyConfig struct {
RedactToolArgs bool `toml:"redact_tool_args"`
// RedactionPatterns are regexes applied to operator-visible text (tool
// previews, event bodies, audit metadata). Nothing is compiled in: unset
// means no text is redacted anywhere. An invalid pattern is a load error.
RedactionPatterns []string `toml:"redaction_patterns"`
// RedactionKeyNames are JSON object keys whose values are elided wholesale
// (case-insensitive substring match). Unset means no key-based redaction.
RedactionKeyNames []string `toml:"redaction_key_names"`
// RedactionPlaceholder replaces each match. Defaults to "[redacted]".
RedactionPlaceholder string `toml:"redaction_placeholder"`
}
PrivacyConfig controls operator-visible redaction of tool I/O. RedactToolArgs defaults to false (show argv/args). Enable via TOML or MIVIA_REDACT_TOOL_ARGS for stricter privacy in shared/recorded sessions.
type ProjectSettings ¶
type ProjectSettings struct {
EnvFile string
BranchPrefix string
SystemPrompt string
Temperature string
MaxTokens string
MaxPromptTokens string
MaxSteps string
RunTimeoutSec int
StoreBackend string
StorePath string
Sandbox bool
RedactToolArgs bool
}
ProjectSettings contains project-scoped configuration for config updates.
type ProviderConfig ¶
type ProviderConfig struct {
// Models is the explicit, finite model catalog for this provider.
Models []ModelSpec `toml:"models,omitempty"`
// DefaultModel is this provider's default model. When Models is non-empty,
// it must be a member of the allowlist.
DefaultModel string `toml:"default_model,omitempty"`
// LegacyModel is a decode sentinel. The old scalar provider model key is
// rejected explicitly so it cannot override an explicit catalog entry.
LegacyModel *string `toml:"model"`
BaseURL string `toml:"base_url"`
APIKeyEnv string `toml:"api_key_env"`
HTTPReferer string `toml:"http_referer"`
XTitle string `toml:"x_title"`
}
ProviderConfig holds non-secret provider settings.
type ProviderModelGroup ¶
type ProviderModelGroup struct {
Provider string
DefaultModel string
Models []ModelSpec
Active bool
Selectable bool
DisabledReason string
}
ProviderModelGroup is a secret-free provider group for the model picker.
type ProviderRuntime ¶
type ProviderRuntime struct {
ProviderName string
BaseURL string
APIKeyEnv string
APIKeySet bool
APIKey string
HTTPReferer string
XTitle string
Models []ModelSpec
}
ProviderRuntime contains resolved provider construction settings. It is not returned by ModelCatalog and must never be rendered or sent to model-facing tools. APIKey is only consumed by the provider factory.
type ProviderSection ¶
type ProviderSection struct {
Name string `toml:"name"`
// PromptCache is "auto" (default) or "off". "auto" enables capture of
// provider-reported prompt-cache usage accounting and, on providers that
// honor explicit markers (openrouter), emission of cache_control markers
// on the stable prefix. "off" disables both. It cannot disable a
// provider's own automatic server-side caching.
PromptCache string `toml:"prompt_cache"`
// StreamIdleTimeoutSeconds bounds the gap between successive bytes on any
// provider read (streaming or non-streaming), once the first byte has
// arrived. Unset (nil) resolves to DefaultStreamIdleTimeoutSeconds
// (100s). This is a process-wide setting, not per-provider: mivia runs
// one active provider configuration per process.
StreamIdleTimeoutSeconds *int `toml:"stream_idle_timeout_seconds"`
// StreamFirstByteTimeoutSeconds bounds the wait for the first byte of a
// provider read, from request-issued. Unset (nil) resolves to
// DefaultStreamFirstByteTimeoutSeconds (240s).
StreamFirstByteTimeoutSeconds *int `toml:"stream_first_byte_timeout_seconds"`
}
ProviderSection selects the active provider.
type ProviderSettings ¶
type ProviderSettings struct {
Name string
BaseURL string
APIKeyEnv string
DefaultModel string
Models []ModelSettings
}
ProviderSettings contains provider information for config updates.
type Resolved ¶
type Resolved struct {
// RedactionPolicy is compiled during Load so an invalid pattern fails at
// startup. Nil means the workspace configured none, which redacts nothing.
RedactionPolicy *redact.Policy
// MaxSteps is nil when unconfigured, so the chat default applies. A
// configured 0 is meaningful (unlimited) and must not be confused with it.
MaxSteps *int
ConfigPath string
EnvFilePath string
EnvFileUsed bool
ProviderName string
Model string
// Models is retained as a compatibility projection of ModelProfiles.
Models []string
// ModelProfiles is the active provider's copied model catalog.
ModelProfiles []ModelSpec
// ProviderRuntimes contains resolved backend material for provider.NewForProvider.
ProviderRuntimes map[string]ProviderRuntime
BaseURL string
APIKeyEnv string
APIKeySet bool
// APIKey is populated only for runtime use; never print it.
APIKey string
HTTPReferer string
XTitle string
SystemPrompt string
MaxPromptTokens *int
// MaxContextTokens is retained as a compatibility projection of the
// selected model's effective prompt budget.
MaxContextTokens int
Temperature *float64
MaxTokens *int
ShowIterationNotices bool
ShowPromptCacheNotices bool
Subagents SubagentConfig
Worktrees WorktreeConfig
StoreBackend string
StorePath string
// StorePathSet reports whether [subagents].store_path was set in the
// selected configuration. It lets repository storage resolve its default
// from the repository root instead of the current worktree.
StorePathSet bool
// Privacy is resolved from [privacy] TOML and MIVIA_REDACT_TOOL_ARGS.
Privacy PrivacyConfig
// Context is the operator's durable storage ceilings, uncapped by default.
Context ContextConfig
// Tools is the resolved tool execution policy.
Tools ToolsConfig
// MCP is the resolved MCP server configuration.
MCP MCPConfig
// MCPWarnings are scrubbed operator diagnostics for the MCP configuration.
MCPWarnings []string
// Memory is the resolved [memory] configuration.
Memory MemoryConfig
// Harness is the resolved [harness] configuration.
Harness HarnessConfig
// Approvals is the resolved [approvals] configuration.
Approvals ApprovalsConfig
// TUI is the resolved [tui] configuration.
TUI TUIConfig
// Workflows is the resolved [workflows] configuration.
Workflows WorkflowsConfig
// Verifiers is the workspace-declared verifier profile set from the
// [verifiers.<name>] tables. The host ships no built-in profiles.
Verifiers map[string]VerifierProfile
// TavilyAPIKey is the Tavily web search API key (set via TAVILY_API_KEY env).
// When set, the search tool uses Tavily as the primary web search engine.
TavilyAPIKey string
// StreamIdleTimeout is the resolved [provider] stream_idle_timeout_seconds,
// defaulted when unset. See ProviderSection.StreamIdleTimeoutSeconds.
StreamIdleTimeout time.Duration
// StreamFirstByteTimeout is the resolved [provider]
// stream_first_byte_timeout_seconds, defaulted when unset. See
// ProviderSection.StreamFirstByteTimeoutSeconds.
StreamFirstByteTimeout time.Duration
// PromptCache is the resolved "auto" or "off" policy for prompt-cache
// usage capture and explicit cache_control markers. Always one of those
// two values after Load - see ProviderSection.PromptCache.
PromptCache string
// contains filtered or unexported fields
}
Resolved is the fully resolved runtime config used by the CLI.
func (*Resolved) AllowsModel ¶
AllowsModel reports whether name may be selected under the resolved policy.
func (*Resolved) ModelCatalog ¶
func (r *Resolved) ModelCatalog() []ProviderModelGroup
ModelCatalog returns a deep copy of the secret-free provider catalog. ReasoningEfforts is cloned per model because cloning the []ModelSpec alone would leave every caller sharing one backing array with the stored catalog.
func (*Resolved) ModelChoices ¶
ModelChoices renders the selectable set for usage and error messages.
func (*Resolved) ModelChoicesFor ¶
ModelChoicesFor renders the selectable catalog for one provider.
func (*Resolved) OtherProvidersWithModel ¶
OtherProvidersWithModel returns the provider names, in ModelCatalog order, of every Selectable provider (other than exclude) whose catalog contains a model named exactly name. Matching is exact (case-sensitive, trimmed), matching AllowsModel's and the model picker's own comparison - model names are provider-declared identifiers, not free text, so this does not normalize case the way a user-facing search would.
This is the single cross-provider model lookup for BOTH the classic REPL (internal/clichat) and the new TUI (internal/uiadapter): those two packages do not import each other, so the shared logic lives here, one level below both, rather than being duplicated (or worse, silently diverging - see the pre-existing bug this fixes: internal/uiadapter's resolveProviderAndModel used to return the FIRST provider whose catalog happened to contain the name, in catalog order, with no check for a second match - a silent, order-dependent provider switch on any name collision). Only Selectable providers are considered: a provider with no API key set is not a switch that would actually work, so it must not appear in a "found under provider X" hint that tells the user to try it.
func (*Resolved) SetModelCatalogForTest ¶
func (r *Resolved) SetModelCatalogForTest(catalog []ProviderModelGroup)
SetModelCatalogForTest sets the model catalog on Resolved for testing purposes.
type SubagentConfig ¶
type SubagentConfig struct {
MaxWorkers int `toml:"max_workers"`
// MaxDepth caps the dependency depth of one orchestrated task graph
// (spawn_agent depends_on chains). 0 means unlimited (default); a
// positive value caps the depth.
MaxDepth int `toml:"max_depth"`
// MaxFanout caps the number of tasks admitted in one orchestration.
// 0 means unlimited (default); a positive value caps the count.
MaxFanout int `toml:"max_fanout"`
DefaultTimeout int `toml:"default_timeout_seconds"`
// DefaultRequestTimeoutSec is the per-LLM-request timeout for subagents
// (seconds). When 0, requestTimeout() uses DefaultSubagentRequestTimeoutSec
// (1800s, 30 minutes) as the per-request context deadline. The 15-minute
// http.Client wall stays the hard per-attempt bound; the 12-hour
// orchestration default no longer feeds individual subagent requests.
DefaultRequestTimeoutSec int `toml:"default_request_timeout_seconds"`
// DefaultTotalTimeoutSec is the whole-subagent wall-clock budget
// (seconds). 0 = unset = DefaultSubagentTotalTimeoutSec (3600s, 60
// minutes). Negative = off: a direct spawn with no per-task timeout then
// has no handler-level bound at all, and workflow or panel steps whose
// own timeout is unset stay bounded only by workflow policy - this is an
// explicit operator opt-out of the last-resort termination guarantee. A
// positive value is the budget itself; a tighter per-task timeout from
// the caller still wins.
DefaultTotalTimeoutSec int `toml:"default_total_timeout_seconds"`
// WireStream opts nested subagent calls into the wire-stream transport:
// the request goes to the provider with stream:true while the call keeps
// its non-stream contract - the full answer is assembled before it comes
// back. Nil means unset, which resolves to true - the pointer exists so
// an explicit `wire_stream = false` is distinguishable from an absent
// key, which a plain bool cannot express. False opts out and keeps every
// nested call on the plain non-stream endpoint. See WireStreamResolved
// for why this default was briefly flipped off and then restored.
WireStream *bool `toml:"wire_stream"`
DefaultBudget int `toml:"default_budget"`
SystemPrompt string `toml:"system_prompt"`
NestedSteps int `toml:"nested_steps"`
// StoreBackend selects the ledger storage backend: "memory" (default) or "sqlite".
StoreBackend string `toml:"store_backend"`
// StorePath is the configured SQLite file path. Chat uses this path for
// sessions, context, worktree routes, and orchestration.
StorePath string `toml:"store_path"`
// HandleRetentionSeconds controls how long completed orchestration run
// handles remain accessible via inspect_agents/join_run/cancel_run
// before automatic eviction. Default: 600 (10 minutes). 0 = no retention.
HandleRetentionSeconds int `toml:"handle_retention_seconds"`
// MaxAuditRounds controls the maximum number of ADLC Step 5 bug audit
// rounds. When 0 (default), rounds are unlimited. Set to a positive
// value to cap.
MaxAuditRounds int `toml:"max_audit_rounds"`
// InlineOutputBytes is the per-task output size threshold (bytes). Task
// results whose output body is at or below this threshold are inlined in
// the model-visible result envelope (the "output" field). Results above
// this threshold emit only "output_ref", "output_bytes", and a bounded
// "synopsis"; the parent fetches the full body via ledger_read.
// Default: 4096. An explicit 0 means "always use refs" (never inline) and
// is preserved through resolution via inlineOutputBytesSet; an absent key
// falls back to the 4096 default. Errors follow the same rule with
// "error"/"error_ref".
InlineOutputBytes int `toml:"inline_output_bytes"`
// SchemaRetryMax is how many corrective re-entries a multi-step child may
// take after an invalid schema-validated reply (plan tools/02). Default 2.
// The initial attempt is separate: retry_max=2 allows two corrective turns.
// Clamped at load: <= 0 means the default 2; a positive value above
// MaxSchemaRetryMax is clamped to it.
SchemaRetryMax int `toml:"schema_retry_max"`
// SpawnStaggerMs staggers the start of each task after the first within
// one dispatch batch by this many milliseconds, so concurrent workers do
// not fire their first provider call on the same instant (the step-1
// thundering-herd hang behind overloaded local proxies). Default: 150.
// An explicit 0 disables staggering and is preserved through resolution
// via spawnStaggerMsSet, mirroring inline_output_bytes; an absent key
// falls back to the default. Values above 1000 are clamped to 1000.
SpawnStaggerMs int `toml:"spawn_stagger_ms"`
// Messaging configures typed agent-to-agent messaging (plan 53). Nested
// under [subagents.messaging]. Always enabled (product decision 2026-08-03).
Messaging MessagingConfig `toml:"messaging"`
// TaskRetry configures automatic retry/backoff for a dispatched task whose
// failure is classified transient (provider.IsTransient - network blip,
// 429, 5xx, timeout) or that timed out. Nested under [subagents.retry].
// Distinct from SchemaRetryMax above, which governs corrective re-entries
// after an invalid schema-validated reply within one task, not whole-task
// retry. All-zero (the default) disables retry entirely, identical to
// today's behavior: a deployment must opt in. See task_retry_config.go
// for the TaskRetryConfig type.
TaskRetry TaskRetryConfig `toml:"retry"`
// contains filtered or unexported fields
}
SubagentConfig holds subagent execution policy and storage configuration.
func (SubagentConfig) WireStreamResolved ¶ added in v0.1.1
func (c SubagentConfig) WireStreamResolved() bool
WireStreamResolved reports the resolved wire-stream switch: absent means on. Read this rather than the pointer so the opt-out default lives in one place.
This default was briefly flipped off as a mitigation after two sessions reported dispatch_tasks batches sticking at "running" with no output. Root-caused (see DefaultMaxBudget in internal/subagents/subagents.go): the actual cause was an unrelated admission-control bug - a 1000 default MaxBudget silently rejecting realistic multi-thousand-per-task budgets before any provider call was ever made (the reported "0ms" failures and "budget limit exceeded"/"run budget exceeded" errors both point directly at it). Confirmed by live reproduction: the exact failing batch (4 tasks, budget 6000 each) succeeds on the first call with wire_stream left on once the budget default is fixed, and a concurrency+stall stress test against wire_stream found no hang (openai_compat_turnstream_concurrency_test.go). Restored to on.
type TUIConfig ¶
type TUIConfig struct {
Theme string `toml:"theme"`
// Mouse is the cockpit's mouse-capture default: true (default)
// enables in-app drag-select, copy, and wheel; false hands the mouse
// to the terminal for native selection. MIVIA_MOUSE overrides it at
// startup; Settings → General changes it live. See
// docs/design/cockpit-research.md rule 6.5.
Mouse *bool `toml:"mouse"`
ShowReasoning *bool `toml:"show_reasoning"`
ScrollLines *int `toml:"scroll_lines"`
ScreenReader *bool `toml:"screen_reader"`
ReducedMotion *bool `toml:"reduced_motion"`
}
TUIConfig controls terminal user interface preferences.
type TaskRetryConfig ¶
type TaskRetryConfig struct {
// MaxRetries is the maximum retry attempts per task. 0 (default) disables
// retry.
MaxRetries int `toml:"max_retries"`
// BaseBackoffSeconds is the initial backoff before the first retry.
BaseBackoffSeconds float64 `toml:"base_backoff_seconds"`
// MaxBackoffSeconds caps the per-retry backoff.
MaxBackoffSeconds float64 `toml:"max_backoff_seconds"`
// BackoffFactor is the exponential multiplier applied after each attempt.
// 0 selects the coordinator's default (2.0) once MaxRetries > 0.
BackoffFactor float64 `toml:"backoff_factor"`
// JitterFraction randomizes each backoff by ±JitterFraction/2. 0 disables
// jitter.
JitterFraction float64 `toml:"jitter_fraction"`
}
TaskRetryConfig is the [subagents.retry] surface for whole-task retry. Fields mirror coordinator.RetryPolicy; internal/config cannot import internal/coordinator (coordinator already imports config), so the CLI wiring layer (internal/cli/orchestration_state.go) converts this into a coordinator.RetryPolicy. All-zero means "no retry" - the safe default.
Retry is NOT safe for a task with side effects it can't undo. A retry re-dispatches the whole task from scratch (a fresh attempt, the same goal and tool access) - it does not skip tool calls the failed attempt already made. A task that writes a file, calls an external API, or sends a message and only THEN hits a late transient error will repeat those side effects on retry. Enable this only for tasks whose tool calls are safe to repeat, or where that risk is acceptable.
type TavilyConfig ¶
type TavilyConfig struct {
// APIKeyEnv overrides the env var name (default "TAVILY_API_KEY").
APIKeyEnv string `toml:"api_key_env"`
// Disable explicitly disables Tavily even if the env var is set.
Disable bool `toml:"disable"`
}
TavilyConfig configures the Tavily web search integration.
type ToolsConfig ¶
type ToolsConfig struct {
// RunAllowlist extends the built-in default allowlist (union).
RunAllowlist []string `toml:"run_allowlist"`
// RunAllowlistOnly replaces the built-in default allowlist entirely.
RunAllowlistOnly []string `toml:"run_allowlist_only"`
// RunBlocklist removes programs from the resolved allowlist (takes precedence).
RunBlocklist []string `toml:"run_blocklist"`
// DisableTools removes built-in tools by name.
DisableTools []string `toml:"disable_tools"`
// EnvAllowlist extends the built-in default env var allowlist (union).
// Entries ending in "*" are treated as prefix rules (e.g. "GIT_*" allows all GIT_ vars).
EnvAllowlist []string `toml:"env_allowlist"`
// EnvAllowlistOnly replaces the built-in default env var allowlist entirely.
// Entries ending in "*" are treated as prefix rules (e.g. "GIT_*" allows all GIT_ vars).
EnvAllowlistOnly []string `toml:"env_allowlist_only"`
// EnvBlocklist removes vars from the resolved env allowlist (takes precedence).
// Entries ending in "*" are treated as prefix rules (e.g. "GIT_*" blocks all GIT_ vars).
EnvBlocklist []string `toml:"env_blocklist"`
// EnvAllowKeywordBlocklist drops variables whose name contains any of these
// substrings even when a prefix rule admitted them. Exact-name entries in
// EnvAllowlist are unaffected, so a build needing one names it explicitly.
EnvAllowKeywordBlocklist []string `toml:"env_allow_keyword_blocklist"`
// RunTimeoutSec is the default timeout for run_command (seconds).
RunTimeoutSec int `toml:"run_timeout_seconds"`
// ToolRunTimeoutSec is the registry-wide default run timeout (seconds)
// the SDK tool-registry backstop applies to tools that declare no
// Capability.Timeout. Positive = that bound. 0 (default) or negative =
// no registry-wide cap (the agent layer maps it to the SDK's
// TimeoutNone). Uncapped is the correct default: the CLI's own
// dispatcher already arms every tool call's Capability.Timeout (or the
// [subagents] default_timeout_seconds fallback) as a real context
// deadline, so the SDK backstop is a second, redundant enforcement
// layer that must never be tighter than the CLI's declared budgets
// unless the operator explicitly asks for it. Without this mapping the
// SDK's hardcoded 10-minute default silently killed long-budget tools
// (dispatch_tasks' 12h orchestration window).
ToolRunTimeoutSec int `toml:"tool_run_timeout_seconds"`
// MaxReadBytes caps read_file output (bytes).
MaxReadBytes int `toml:"max_read_bytes"`
// MaxEditFileBytes caps the file size editable in place by search_replace and multi_edit (bytes).
// 0 means uncapped today; expected to become an explicit opt-out once the derived budget profile ships.
MaxEditFileBytes int `toml:"max_edit_file_bytes"`
// MaxWriteKB caps write_file content (KiB).
MaxWriteKB int `toml:"max_write_kb"`
// MaxOutputBytes caps run_command output (bytes).
MaxOutputBytes int `toml:"max_output_bytes"`
// MaxListDirEntries caps list_dir output.
MaxListDirEntries int `toml:"max_list_dir_entries"`
// MaxToolResultBytes caps each tool result stored in agent-loop history
// (bytes), applied identically to the interactive session loop and nested
// sub-agent loops. 0 (the default) means uncapped: per-tool budgets are
// the bound. Positive values below 1024 are rejected at load.
MaxToolResultBytes int `toml:"max_tool_result_bytes"`
// BatchResultBudgetBytes bounds what ONE tool batch may add to history,
// across all of its parallel calls together. max_tool_result_bytes bounds
// each call in isolation and cannot see the others, so N calls that are
// each honestly under it still blow the context when they land in the same
// step; this is the only bound that sees the batch as a whole.
//
// nil (absent) resolves to the derived budget; explicit 0 disables it;
// any negative derives it from the model's prompt budget (a quarter of
// it, floor 256 KiB; inert when there is no prompt budget). A positive
// value is the literal byte budget and must be at least 16 KiB - below
// that every batch would degrade to references.
//
// Over-budget results are degraded to content references, never failed:
// the model already paid for the calls and their side effects already
// happened.
BatchResultBudgetBytes *int `toml:"batch_result_budget_bytes"`
// RefOnlyTools is an opt-in list of tool names whose results are always
// spooled and replaced by a ref-only notice, never inlined whole; empty
// = off.
RefOnlyTools []string `toml:"ref_only_tools"`
// MaxTavilyResponseBytes bounds the bytes read from a Tavily API response
// body - the `search` tool's Tavily path and `extract`. It is NOT a
// truncation cap: the tools never cut content. The bound exists so their
// maximum output is a finite, declarable number, which the dispatcher's
// output backstop is derived from; a response over the bound is refused
// with an explicit error naming this key. 0 or negative resolves to the
// built-in default (never "unlimited" - an unlimited response could not be
// declared, and undeclared output is what the backstop destroys). Values
// outside [1024, 64 MiB] are rejected at load.
MaxTavilyResponseBytes int `toml:"max_tavily_response_bytes"`
// MaxFetchKB bounds the body read by fetch_url (KiB). Default 4096 (4 MiB).
// 0 means unlimited. Unlimited is safe for fetch_url because it truncates
// an over-bound body instead of refusing it - unlike the Tavily bound, an
// unbounded read still yields a bounded, usable result, so nothing the
// dispatcher derives from a declared budget depends on this number.
MaxFetchKB int `toml:"max_fetch_kb"`
// MemoryBackstopMB is the OOM guard (MiB) for tools that may load whole
// files into memory when volume caps are uncapped (read/edit/list budgets).
// Shipped default 256. This is NOT a context-cost cap. 0 or negative
// resolves to the default so the guard cannot be accidentally disabled.
MemoryBackstopMB int `toml:"memory_backstop_mb"`
// RedactToolArgs hides argv from operator-visible output.
RedactToolArgs bool `toml:"redact_tool_args"`
// SecretPathPatterns replaces the hard-coded secret path blocklist.
SecretPathPatterns []string `toml:"secret_path_patterns,omitempty"`
// SecretPathExceptions adds exceptions to the secret path blocklist.
SecretPathExceptions []string `toml:"secret_path_exceptions,omitempty"`
// Core is the always-advertised tool tier (plan tools/05). nil (the key
// omitted) keeps every authorized tool core, which is byte-identical to the
// behavior before deferred loading existed. When set, tools outside it are
// deferred: their schemas are advertised (plan tools-advertising/01) but
// locked for execution until the model loads them with load_tools.
// Naming a tool here never grants authority - the list is intersected
// with the agent's effective tool set.
Core *[]string `toml:"core,omitempty"`
// SearchIgnorePatterns adds directory/file names to skip during grep/glob walks.
// Extends the built-in defaults (.git, node_modules, vendor). Does not replace them.
SearchIgnorePatterns []string `toml:"search_ignore_patterns,omitempty"`
// WritePathBlocklist names workspace-relative paths or directories whose
// write tools refuse to change. There is no compiled-in default
// (DefaultWritePathBlocklist is empty); protection is opt-in. Removal of
// an entry named here is via WritePathBlocklistRemove. Entries use
// forward slashes and are normalized
// (trimmed, cleaned) at load; an entry that is empty, ".", or absolute is
// a load error.
// The blocklist applies to workflow agent steps, whose write tools
// (write_file, search_replace, multi_edit, delete_file) refuse listed
// paths. The interactive session registry does not enforce it.
WritePathBlocklist []string `toml:"write_path_blocklist,omitempty"`
// WritePathBlocklistRemove removes workspace-relative paths or directories
// from the effective write-path blocklist (the built-in defaults plus
// WritePathBlocklist) for workflow agent steps. It is the explicit opt-out
// for a default entry (.git, .mivia/mivia.toml) or a project addition, and
// it is the only way to unblock those two defaults. Unblocking a path is a
// trust decision: .mivia/mivia.toml carries this very blocklist, so an
// agent that can edit it can remove its own restrictions (including
// .mivia/agents, .mivia/policy, .mivia/workflows, go.mod, go.sum, and
// go.work), and .git carries commit
// history and hooks, so an agent that can edit it can rewrite history or
// plant hooks that bypass the host hook-policy gates. Keep the defaults
// blocked unless a host-owned step genuinely needs the path. Entries use
// the same normalization and validation rules as WritePathBlocklist; an
// entry listed in BOTH keys is a load error (the keys contradict).
WritePathBlocklistRemove []string `toml:"write_path_blocklist_remove,omitempty"`
// MaxInspectRepositoryBytes caps the inspect_repository result envelope
// (bytes). Unlike MaxReadBytes/MaxOutputBytes there is no uncapped state:
// the tool's output must always be valid, bounded JSON. 0 or negative
// resolves to the built-in 64 KiB default. Values outside
// [MinInspectRepositoryBytes, MaxInspectRepositoryBytes] are rejected at load.
MaxInspectRepositoryBytes int `toml:"max_inspect_repository_bytes"`
// DiagnosticsCommand is the DEPRECATED alias for the reserved "default"
// entry of DiagnosticsCommands. It is the argv of the project diagnostics
// command the get_diagnostics tool runs. resolveToolsConfig folds a set
// alias into DiagnosticsCommands["default"] and then clears it, so
// Validate (and every consumer) sees exactly one surface; setting BOTH
// the alias and the map is ambiguous and rejected at load. Empty (unset
// or []) means the tool is not registered. When set, argv[0] must be a
// bare program name on the resolved run allowlist (run_allowlist_only
// when set, else run_allowlist); validation rejects anything else at
// load, mirroring the run_command gate, so a diagnostics tool that could
// never register cannot load clean.
DiagnosticsCommand []string `toml:"diagnostics_command"`
// DiagnosticsCommands maps a command name to the argv of a project
// diagnostics command the get_diagnostics tool runs. Empty or unset means
// the tool is not registered; "default" is the reserved name for the
// deprecated diagnostics_command alias (declaring it explicitly is
// allowed). Every entry is validated at load like the run_command gate:
// non-empty argv, argv[0] a bare program name on the EFFECTIVE run
// allowlist (run_allowlist_only when set, else run_allowlist, minus
// run_blocklist - the tools layer subtracts the blocklist in
// configuredRunAllowlist, so a command that could never register is a
// load error). Command names must be non-empty, non-whitespace, and
// unique after case folding.
DiagnosticsCommands map[string][]string `toml:"diagnostics_commands"`
}
ToolsConfig configures tool execution policies.
type VerifierCommand ¶
VerifierCommand is one sandboxed command of a declared profile. Program is a bare executable name; Args are argv verbatim, never a shell string.
type VerifierProfile ¶
type VerifierProfile struct {
// GoModuleBaseline marks a profile whose commands read Go module files.
// When any referenced profile sets it, admission captures go.mod/go.sum
// and the sandbox pins them so a workflow agent cannot change what the
// gate builds against.
GoModuleBaseline bool
Commands []VerifierCommand
}
VerifierProfile is one [verifiers.<name>] table: an ordered command list and whether the profile needs the pinned Go module baseline.
type WorkflowPanelLimits ¶
type WorkflowPanelLimits struct {
MemberMaxOutputPerCall *int `toml:"member_max_output_per_call"`
MemberMaxToolCalls *int `toml:"member_max_tool_calls"`
SynthesisMaxOutputPerCall *int `toml:"synthesis_max_output_per_call"`
SynthesisMaxToolCalls *int `toml:"synthesis_max_tool_calls"`
// MemberDeadlineDefaultSeconds overrides the wall-clock default a
// panel member attempt gets when the workflow declares no run
// deadline (max_duration_seconds = 0). Seconds, matching
// definition.Limits.MaxDurationSeconds' unit.
MemberDeadlineDefaultSeconds *int `toml:"member_deadline_default_seconds"`
}
WorkflowPanelLimits overrides the compiled defaults every agent_panel step's member and synthesis children run under (internal/workflows/controller.DefaultPanelLimits, applied in buildPanelAttempt/buildPanelSynthesisWork). A nil field keeps the compiled default; this mirrors the [chat] max_steps *int nil-means-default pattern (ChatConfig.MaxSteps above), not a new convention.
type WorkflowsConfig ¶
type WorkflowsConfig struct {
Panels WorkflowPanelLimits `toml:"panels"`
}
WorkflowsConfig holds workflow-engine defaults.
type WorktreeConfig ¶
type WorktreeConfig struct {
// BranchPrefix is the prefix for branches that mivia creates for worktrees.
BranchPrefix string `toml:"branch_prefix"`
}
WorktreeConfig controls worktree branch settings.
func LoadWorktreeConfig ¶
func LoadWorktreeConfig(mainRepoRoot string) (WorktreeConfig, error)
LoadWorktreeConfig reads the worktree settings from the main repository. It does not use general config discovery. A linked worktree therefore uses the main repository setting, not its current directory or MIVIA_CONFIG.
Source Files
¶
- agents.go
- agents_diagnostics.go
- agents_io.go
- agents_links_unix.go
- agents_parse.go
- approvals_config.go
- bootstrap.go
- chat_config_persist.go
- defaults.go
- envlookup.go
- harness_config.go
- hooks_scope.go
- load.go
- loopback.go
- mcp.go
- memory.go
- messaging_resolve.go
- messaging_types.go
- model_keys.go
- model_spec.go
- models.go
- paths.go
- persist_lock.go
- policy_resolve.go
- prompt_budget.go
- provider_config_persist.go
- provider_runtime.go
- settings_persist.go
- settings_persist_projects.go
- task_retry_config.go
- tool_result_warn.go
- tools_config.go
- types.go
- types_memory.go
- validate.go
- verifiers_config.go
- worktree_config.go
- write_path_blocklist.go