config

package
v0.69.2 Latest Latest
Warning

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

Go to latest
Published: Jul 17, 2026 License: MIT Imports: 22 Imported by: 0

Documentation

Index

Constants

View Source
const (
	ConnectionDialTimeoutDefault            = 500 * time.Millisecond
	ConnectionHandshakeTimeoutDefault       = 5 * time.Second
	ConnectionStartTimeoutDefault           = 5 * time.Second
	ConnectionStartPollIntervalDefault      = 50 * time.Millisecond
	ConnectionReconnectTimeoutDefault       = 10 * time.Second
	ConnectionReconnectIntervalDefault      = 250 * time.Millisecond
	ConnectionRemoteDialTimeoutDefault      = 10 * time.Second
	ConnectionRemoteHandshakeTimeoutDefault = 15 * time.Second
	ConnectionRemotePairingTimeoutDefault   = 11 * time.Minute
)

Connection timing defaults. Each mirrors the fixed value that governed the behaviour before issue #1242 made the policy configurable.

View Source
const (
	HeadlessMaxLineBytesDefault     = 16 * 1024 * 1024
	HeadlessControlTimeoutDefault   = 30 * time.Second
	HeadlessInterruptTimeoutDefault = 5 * time.Second
	HeadlessPreviewBytesDefault     = 16 * 1024
)

Headless processing-limit defaults. Each mirrors the fixed constant that governed the behaviour before issue #1250 made the limits configurable.

View Source
const (
	TranscriptMaxContextBytesDefault    = 256 * 1024
	TranscriptMaxToolOutputBytesDefault = 4 * 1024
	TranscriptMaxLineBytesDefault       = 16 * 1024 * 1024
	TranscriptMaxMetadataBytesDefault   = 4 * 1024 * 1024
)

Transcript processing-limit defaults. Each mirrors the fixed constant that governed the transcript reader/renderer before issue #1250.

View Source
const (
	RemoteMaxPendingPairingsDefault = 16
	RemoteMaxPendingPairingsMax     = 1024
	RemotePendingPairingTTLDefault  = 10 * time.Minute
	RemotePendingPairingTTLMin      = time.Minute
	RemotePendingPairingTTLMax      = 24 * time.Hour
	RemotePairFallbackCountDefault  = 5
	RemotePairFallbackCountMax      = 1000
	RemotePairFallbackWindowDefault = time.Minute
	RemotePairFallbackWindowMin     = time.Second
	RemotePairFallbackWindowMax     = 24 * time.Hour
)

Pairing policy bounds. Defaults preserve the historically-fixed values (16 pending, 10m TTL, 5/min fallback rate); the ceilings/floors keep an operator override from disabling anti-flood protection or pinning requests forever.

View Source
const (
	OrchestratorInitialBackoffDefault      = 2 * time.Second
	OrchestratorMaxBackoffDefault          = 300 * time.Second
	OrchestratorMultiplierDefault          = 2.0
	OrchestratorStableResetDefault         = 60 * time.Second
	OrchestratorFreshStartThresholdDefault = 3
)

Orchestrator restart defaults. The default schedule preserves graith's historical backoff curve exactly; the geometric defaults apply only when a user clears Schedule to opt into computed backoff.

View Source
const (
	LaunchMaxConcurrentDefault    = 3
	LaunchStartupTimeoutDefault   = 3 * time.Minute
	LaunchSettleTimeoutDefault    = 10 * time.Second
	LaunchMaxRestartsDefault      = 3
	LaunchWatchdogIntervalDefault = 15 * time.Second
	LaunchSlotPollIntervalDefault = 100 * time.Millisecond
)

Launch tuning defaults. MaxConcurrent defaults to 3 because the #1092 evidence showed ~4 concurrent startups completing fine while the 5th stalled.

View Source
const (
	ConvertSettleTimeoutDefault     = 5 * time.Second
	ConvertKillTimeoutDefault       = 3 * time.Second
	ConvertForceKillTimeoutDefault  = 3 * time.Second
	MassExitWindowDefault           = 2 * time.Second
	MassExitThresholdDefault        = 5
	ProcessKillGraceDefault         = 5 * time.Second
	AdoptedTimeoutDefault           = 24 * time.Hour
	AdoptedPollIntervalDefault      = time.Second
	ScrollbackHydrationBytesDefault = 128 * 1024
	InputDelayDefault               = 50 * time.Millisecond
	DefaultColsDefault              = 80
	DefaultRowsDefault              = 24
	MaxLogBytesDefault              = 100 * 1024 * 1024
)

Lifecycle policy defaults. Each mirrors the fixed constant or bare literal that governed the behaviour before issue #1243 made the policy configurable.

View Source
const (
	DetectionScanIntervalDefault       = 500 * time.Millisecond
	DetectionFetchIntervalDefault      = 5 * time.Minute
	DetectionFetchTimeoutDefault       = 30 * time.Second
	DetectionSilentThresholdDefault    = 20 * time.Second
	DetectionAdoptedGraceDefault       = 60 * time.Second
	DetectionRecentOutputWindowDefault = 3 * time.Second
	DetectionHookStartWindowDefault    = 5 * time.Second
	DetectionHookActivityWindowDefault = 30 * time.Second
	DetectionHookTerminalWindowDefault = 30 * time.Minute
)

Detection timing defaults. Each mirrors the fixed constant that governed the behaviour before issue #1241 made the policy configurable.

View Source
const (
	TokenPollIntervalDefault = 30 * time.Second
	TokenStartupDelayDefault = 5 * time.Second
	TokenBatchSizeDefault    = 8
)

Token-accounting defaults mirror the fixed constants that governed the loop before issue #1244 made the policy configurable.

View Source
const (
	ResourceSampleIntervalDefault = 30 * time.Second
	ResourceSampleHistoryDefault  = 5
)

Resource-monitor defaults mirror the fixed constants that governed the loop before issue #1244 made the policy configurable.

View Source
const (
	LimitsLogLinesDefault             = 300
	LimitsWaitScanLinesDefault        = 500
	LimitsWaitBufferBytesDefault      = 64 * 1024
	LimitsMCPLogReadBytesDefault      = 1 << 20 // 1 MiB
	LimitsApprovalDisplayBytesDefault = 500
	LimitsLastMessageRunesDefault     = 2000
	LimitsInboxPreviewBytesDefault    = 1000
)

Limits defaults mirror the fixed constants and literals that governed each surface before issue #1252 unified them.

View Source
const (
	TerminalRefreshIntervalDefault = 2 * time.Second
	TerminalSummaryWidth           = 40
)

Terminal presentation defaults mirror the fixed literals that governed the behaviour before issue #1254 made the policy configurable.

View Source
const (
	// PRWatchKickChannelSizeDefault absorbs a modest burst of git-ref events;
	// timer polling remains the fallback when the best-effort channel is full.
	PRWatchKickChannelSizeDefault = 64
	// PRWatchKickChannelSizeMax bounds startup allocation for the channel. A
	// 4096-entry buffer is ample for large fleets while keeping the allocation
	// small and predictable even when configuration is untrusted.
	PRWatchKickChannelSizeMax = 4096
)
View Source
const (
	MessagesConversationPageSizeDefault = 500
	MessagesConversationMaxLimitDefault = 2000
	MessagesJailListLimitDefault        = 2000
	MessagesSubscriberBufferDefault     = 64
	MessagesBusyTimeoutDefault          = 5 * time.Second
)

Messages operational-limit defaults, mirroring the fixed literals that governed the message log before issue #1249 made them configurable.

View Source
const (
	MessagesConversationMaxLimitCeiling = 100_000
	MessagesJailListLimitCeiling        = 100_000
	MessagesSubscriberBufferCeiling     = 65_536
	MessagesBusyTimeoutCeiling          = 5 * time.Minute
)

Hard safety ceilings for the [messages] operational limits. Config may tune a value up to (but not past) its ceiling; Validate rejects anything above it so a typo can't request an absurd allocation, unbounded sort, or an effectively infinite lock wait.

View Source
const (
	// DefaultPurgeStartupDelay is how long after startup the first purge sweep
	// runs, catching windows that expired while the daemon was down without
	// racing the rest of daemon initialisation.
	DefaultPurgeStartupDelay = 30 * time.Second
	// DefaultPurgeInterval is how often the purge sweep runs after the first.
	DefaultPurgeInterval = 10 * time.Minute
)

Purge-loop scheduling defaults, used when the matching [delete] key is unset. The window they sweep is measured in hours, so the cadence is deliberately coarse: purging a little late is harmless, and only the frozen ExpiresAt (not this timing) decides whether a session is recoverable.

View Source
const (
	TodoEmitScenario = "scenario" // emit only for scenario-scoped lists (default)
	TodoEmitAll      = "all"      // emit for every scope
	TodoEmitOff      = "off"      // never emit
)

Emit-events modes for the task-list subsystem.

View Source
const (
	TodoMaxTitleDefault      = 500
	TodoMaxNoteDefault       = 2000
	TodoListLimitDefault     = 2000
	TodoSweepIntervalDefault = time.Minute
	TodoBusyTimeoutDefault   = 5 * time.Second
)

Task-list ([todo]) operational-limit defaults, mirroring the fixed literals that governed the store before issue #1249 made them configurable.

View Source
const (
	TodoMaxTitleCeiling    = 500
	TodoMaxNoteCeiling     = 2000
	TodoListLimitCeiling   = 100_000
	TodoBusyTimeoutCeiling = 5 * time.Minute
)

Hard safety ceilings for the [todo] operational limits. TodoMaxTitleCeiling and TodoMaxNoteCeiling equal the database CHECK constraints baked into the schema at creation — config may tighten below them but never past them, so a configured limit can never exceed what the database will accept. The others bound allocation and lock-wait time.

View Source
const (
	NotifyCoalesceWindowDefault     = 30 * time.Second
	NotifyDispatchTimeoutDefault    = 15 * time.Second
	NotifyInboxIdleTimeoutDefault   = 10 * time.Second
	NotifyInboxMaxWaitDefault       = 2 * time.Minute
	NotifyInboxCooldownDefault      = 30 * time.Second
	NotifyInboxDetachedDelayDefault = 5 * time.Second
)

Notification timing defaults. Each mirrors the fixed constant that governed the behaviour before issue #1245 made the policy configurable.

View Source
const (
	NotifyPriorityLow    = "low"
	NotifyPriorityNormal = "normal"
	NotifyPriorityHigh   = "high"
)

Notification priority levels for `gr notify`.

View Source
const (
	PromptInjectionAppendSystemPrompt    = "append_system_prompt"
	PromptInjectionCursorRules           = "cursor_rules"
	PromptInjectionDeveloperInstructions = "developer_instructions"
	PromptInjectionNone                  = "none"
)

Valid values for [agents.<name>].prompt_injection. Each names a prompt delivery mechanism graith owns: append_system_prompt is Claude's --append-system-prompt flag, cursor_rules writes a .cursor/rules file, developer_instructions is Codex's -c developer_instructions override, and none suppresses injection. graith owns this enum (it maps to graith's own launch behaviour), so an unknown value is a config error rather than a silent no-op. See issue #1232.

View Source
const (
	ScenarioCleanupOff       = "off"
	ScenarioCleanupOnSuccess = "on_success"
	ScenarioCleanupAlways    = "always"
)
View Source
const (
	TrackerStateOpen   = "open"
	TrackerStateClosed = "closed"
	TrackerStateAll    = "all"
)

Tracker active-state values for TrackerConfig.ActiveState.

View Source
const (
	TrackerReapStop   = "stop"   // stop the agent (recoverable via gr resume)
	TrackerReapDelete = "delete" // soft-delete the session (recoverable via gr restore)
	TrackerReapNone   = "none"   // leave the session; report only
)

Tracker reap-policy values for TrackerConfig.Reap.

View Source
const (
	ActionCommand  = "command"
	ActionSession  = "session"
	ActionScenario = "scenario"
	ActionMessage  = "message"
	ActionTracker  = "tracker"
)

Action type values for ActionConfig.Type.

View Source
const (
	OverlapSkip  = "skip"
	OverlapAllow = "allow"
	OverlapQueue = "queue" // deferred to v2
)

Overlap policy values for TriggerPolicy.Overlap.

View Source
const (
	CleanupAlways    = "always"     // delete on any stop
	CleanupOnSuccess = "on_success" // delete only on a clean (exit 0) stop
)

Auto-cleanup mode values for a session action's AutoCleanup.

View Source
const ConfigReloadDebounceDefault = 200 * time.Millisecond

ConfigReloadDebounceDefault is the quiet period the config-file watcher waits after the last write before reloading, used when config reload_debounce is unset. It coalesces an editor's write-truncate-write burst into one reload.

View Source
const DefaultDeleteRetention = 24 * time.Hour

DefaultDeleteRetention is the soft-delete retention window used when [delete] retention is unset.

View Source
const DefaultGCOrphanMinAge = 5 * time.Minute

DefaultGCOrphanMinAge is the minimum age an orphaned worktree/scratch directory must have before GC will remove it, used when [gc] orphan_min_age is unset. Directories are created early in a session's lifecycle (during StatusCreating, before the session is committed to state), so a young directory may belong to an in-flight create that GC would otherwise race and destroy — the floor is a safety margin, not a cosmetic delay.

View Source
const DefaultNotifyMaxPerHour = 12

DefaultNotifyMaxPerHour is the rolling-hour cap on low/normal push notifications used when [notifications] max_per_hour is unset.

View Source
const DefaultRemotePort = 4823

DefaultRemotePort is the TCP port the tailnet control listener binds when [remote] port is unset, and the default the `gr remote pair` client dials. It is the single source of truth for the port on the Go side; the embedded default_config.toml carries the same value (kept in lockstep by a test) and the Swift clients mirror it via GraithTransport.defaultRemotePort.

View Source
const DefaultTodoClaimLease = 30 * time.Minute

DefaultTodoClaimLease is the default claim-lease window: an in-progress item whose owner has made no progress for this long is auto-reopened. 0 disables.

View Source
const (
	GCXEventOnCallAlertGroup = "oncall_alert_group"
)
View Source
const MigrationHealthWindowDefault = 1500 * time.Millisecond

MigrationHealthWindowDefault is how long Migrate waits to confirm the target agent survived startup before declaring the migration successful, used when [migration] health_window is unset (issue #1250).

View Source
const RedactedMask = "***"

RedactedMask is the placeholder substituted for secret-bearing values when a config is rendered for a caller that must not see raw secrets.

View Source
const ReservedTriggerNamePrefix = "scenario:"

ReservedTriggerNamePrefix is reserved for the daemon's namespaced scenario-embedded trigger names (scenario:<id>:<name>). A config-origin trigger name must not use it, or it would be misrouted to a scenario lookup.

View Source
const SQLiteBusyTimeoutResolution = time.Millisecond

SQLiteBusyTimeoutResolution is the smallest busy_timeout SQLite can honour. The pragma takes a whole number of milliseconds, so any positive duration below 1ms would render as busy_timeout(0), which disables lock waiting entirely. Both the messages and todo busy timeouts are validated and clamped to at least this resolution (see #1322).

View Source
const (
	TrackerProviderGitHub = "github"
)

Tracker provider values for TrackerConfig.Provider.

Variables

View Source
var DefaultTrustedAssociations = []string{"OWNER", "MEMBER", "COLLABORATOR"}

DefaultTrustedAssociations is the trusted author_association set used when pr_watch.trusted_author_associations is unset. It is the "has write access to, or is a member of the org that owns, the repo" tier; CONTRIBUTOR is excluded deliberately (on a public repo it means only "merged a commit once", and bots can carry it — see the author-trust design doc).

View Source
var DefaultWatchBuiltinIgnores = []string{".git/", ".git", ".hg/", ".svn/", "*.swp", "*.swx", "4913", ".DS_Store"}

DefaultWatchBuiltinIgnores is the daemon-wide set of directories/patterns never watched by a file-watch trigger when [triggers.advanced] watch_builtin_ignores is unset. Watching any of these is never useful and they are prime feedback-loop / churn sources. Materialized in default_config.toml.

View Source
var SandboxSignalModes = []string{"isolated", "allow_same_sandbox", "allow_all"}

SandboxSignalModes are the accepted values for [sandbox] signal_mode. They mirror nono v0.66.0's security.signal_mode enum. Empty is also valid (inherit nono's base-profile default).

Functions

func DefaultAgentPrompt added in v0.35.0

func DefaultAgentPrompt() string

func DefaultTOML added in v0.19.0

func DefaultTOML() []byte

func DiffFromDefaults added in v0.68.5

func DiffFromDefaults(cfg *Config, toLabel string) (string, error)

DiffFromDefaults returns a unified diff (built-in defaults → cfg) of the two TOML renderings. toLabel names the "to" side in the diff header (e.g. the config file path, or "effective"). An empty return means cfg's effective rendering is byte-for-byte identical to the built-in defaults' rendering.

func EffectiveTOML added in v0.68.5

func EffectiveTOML(cfg *Config) ([]byte, error)

EffectiveTOML renders cfg as TOML — the effective, fully-merged configuration (built-in defaults overlaid with the user's file). This is what `gr config show` prints and what the GUI's config viewer displays.

func Expand

func Expand(s string, vars TemplateVars) (string, error)

func ExpandPath added in v0.11.0

func ExpandPath(p string) string

func ExpandPathRelative added in v0.66.0

func ExpandPathRelative(p, baseDir string) string

ExpandPathRelative resolves a configured path deterministically: it expands a leading ~/, and resolves a still-relative path against baseDir (the directory holding the config file) rather than the process working directory, then cleans the result. This keeps a value like [approvals.builtin] config resolving to the same absolute path regardless of which directory the daemon or CLI happens to run from. An empty (or whitespace-only) path stays empty so callers can distinguish "unset" from a resolved path.

func ExpandSlice

func ExpandSlice(ss []string, vars TemplateVars) ([]string, error)

func ExpandTrigger added in v0.67.0

func ExpandTrigger(s string, vars TriggerVars) (string, error)

ExpandTrigger replaces {token} occurrences in s using the trigger variable set. An unknown token is an error (parity with Expand's discipline).

func IncludeEnvVarName added in v0.19.0

func IncludeEnvVarName(repoBasename string) string

func IsTemplateVar added in v0.69.1

func IsTemplateVar(name string) bool

IsTemplateVar reports whether name is a known template variable (one of the keys TemplateVars expands). Used to validate an agent's option_args `when` gate so a typo (`when = "reasoning"`) is caught at config-load time rather than silently never firing.

func LegacyRuntimeDirs added in v0.11.0

func LegacyRuntimeDirs() []string

LegacyRuntimeDirs returns paths where older versions stored the socket and PID file (TMPDIR or /tmp fallbacks). Used during startup to detect and clean up an orphaned daemon after the socket location changed.

func NormalizeNotifyPriority added in v0.67.2

func NormalizeNotifyPriority(p string) (string, bool)

NormalizeNotifyPriority resolves a user-supplied priority to a canonical level, defaulting an empty value to "normal". It reports ok=false for an unrecognised value so callers can reject it.

func ParseDurationWithDays added in v0.3.0

func ParseDurationWithDays(s string) (time.Duration, error)

func ResolveConfigPath added in v0.66.0

func ResolveConfigPath(explicit string) (path string, exists bool, err error)

ResolveConfigPath returns the config file that LoadOrDefault(explicit) would read and whether that file exists on disk. When explicit is set it is used verbatim. When empty, resolution mirrors LoadOrDefault: the profile/XDG path, falling back to the legacy macOS path only when the XDG file is absent and no profile is active. Diagnostics (e.g. gr doctor) use this so the reported and inspected file is the same one the CLI/daemon actually load.

func ResolvePath added in v0.16.5

func ResolvePath(p string) string

func ResolveProfile added in v0.18.0

func ResolveProfile() (profile string, appName string, err error)

func ValidPromptInjection added in v0.69.1

func ValidPromptInjection(s string) bool

ValidPromptInjection reports whether s is empty (name-based fallback) or one of the known prompt_injection method names.

func ValidateIncludes added in v0.68.7

func ValidateIncludes(mainRepoPath string, includes []string) error

ValidateIncludes checks a set of include paths against the main repo for the collisions that would break the worktree/env-var layout: an include equal to the main repo, duplicate basenames (across the main repo and the includes), and generated GRAITH_INCLUDE_* env-var name collisions. Included worktrees and their env vars are keyed by basename, so these must be unique. Used both by repo-config validation and by the session-create path for scenario-supplied includes (issue #1046), so both surfaces reject the same footguns up front rather than failing with a low-level git error mid-setup.

func ValidateScenarioLifecycle added in v0.69.2

func ValidateScenarioLifecycle(c ScenarioLifecycleConfig) error

func ValidateTriggerStructure added in v0.68.5

func ValidateTriggerStructure(where string, t *TriggerConfig) []error

ValidateTriggerStructure runs the config-independent structural validation for a single trigger: exactly one source, the source's own rules, the action's shape, and the policy. Config-dependent checks (allowed_repo_paths and [orchestrator] enabled) are layered on separately by validateActionConfigDeps. It is exported so the scenario-file loader can hold scenario-embedded [[trigger]] blocks to the same shape rules without a full *Config.

Types

type ActionConfig added in v0.67.0

type ActionConfig struct {
	Type string `toml:"type"` // command | session | scenario | message | tracker

	// command:
	Command  string `toml:"command"`
	Repo     string `toml:"repo"`     // required for schedule commands; rejected for watch
	Timeout  string `toml:"timeout"`  // max run time; default 5m
	Mutating bool   `toml:"mutating"` // may write its execution root; rejected in v1
	Sandbox  *bool  `toml:"sandbox"`  // nil => default true; false runs unconfined
	// SandboxConfig is extra sandbox grants merged onto the base command profile,
	// mirroring the MCP-server pattern (MCPServerConfig.SandboxConfig).
	SandboxConfig *SandboxConfig `toml:"sandbox_config"`

	// session:
	Prompt string `toml:"prompt"`
	Agent  string `toml:"agent"`
	Model  string `toml:"model"`
	Ensure bool   `toml:"ensure"` // idempotent ensure-reviewer (watch source only)
	// AutoCleanup soft-deletes a trigger-spawned session once it stops, so a
	// finished briefing/report session doesn't clutter `gr list`. It is a union
	// of bool and string: absent/false/"" disables it; true (or "always")
	// deletes on any stop; "on_success" deletes only on a clean (exit 0) stop.
	// Decoded as any so TOML can supply either a bool or the string enum; use
	// AutoCleanupMode to normalise. Session action only.
	AutoCleanup any `toml:"auto_cleanup"`
	// IdleTimeout auto-stops the spawned session after it sits idle (agent at
	// rest, no attached client) this long, overriding the agent default. A Go
	// duration ("1m", "5m"). Session action only. When unset, an
	// auto_cleanup="always" session defaults to a short idle window so a finished
	// briefing reaps itself promptly (finish -> idle-stop -> soft-delete); see
	// SessionIdleTimeout.
	IdleTimeout string `toml:"idle_timeout"`

	// scenario:
	Scenario string `toml:"scenario"`

	// tracker: keep live sessions in sync with an issue tracker. On each
	// scheduled fire the daemon polls the tracker for active issues and
	// reconciles sessions against them — spawning one per active issue (seeded
	// with the templated Prompt above) and reaping the session when its issue
	// leaves the active state. Schedule source only. See TrackerConfig and
	// docs/design/2026-07-16-tracker-poll-action.md.
	Tracker *TrackerConfig `toml:"tracker"`

	// message:
	Body string `toml:"body"`

	// notify (any action type): when NotifyOnComplete is set, the daemon fires a
	// proactive push notification (see [notifications]) once the action finishes
	// firing. NotifyMessage is the body (templated with the trigger vars;
	// defaults to a generic "<name> completed"); NotifyPriority is low/normal/high
	// (defaults to normal, or high when the action errored).
	NotifyOnComplete bool   `toml:"notify_on_complete"`
	NotifyMessage    string `toml:"notify_message"`
	NotifyPriority   string `toml:"notify_priority"`

	Deliver DeliverConfig `toml:"deliver"`
}

ActionConfig is the shared action vocabulary. Type selects the verb.

func (ActionConfig) AutoCleanupMode added in v0.67.2

func (a ActionConfig) AutoCleanupMode() (string, error)

AutoCleanupMode normalises the auto_cleanup union to "" (disabled), CleanupAlways, or CleanupOnSuccess. true is shorthand for "always"; false and an absent value are disabled. Any other value is a config error.

func (ActionConfig) RepoPath added in v0.67.2

func (a ActionConfig) RepoPath() string

RepoPath returns the action's configured repo canonicalised the same way sessions and the store CLI treat a repo path: a leading ~/ expanded, made absolute, and symlinks resolved (via ResolvePath). This matters for repo-store delivery, whose namespace is keyed off the repo path — a raw ~/... or a symlinked spelling would otherwise scope to a different store than the one agents read. It returns "" when no repo is set — unlike ResolvePath/ExpandPath, which would resolve "" to the working directory — so callers can still distinguish "unset" (shared store / no execution root) from a resolved path.

func (ActionConfig) Sandboxed added in v0.67.0

func (a ActionConfig) Sandboxed() bool

Sandboxed reports whether a command action runs sandboxed (nil => true).

func (ActionConfig) SessionIdleTimeout added in v0.67.2

func (a ActionConfig) SessionIdleTimeout() (time.Duration, error)

SessionIdleTimeout resolves the idle-stop window for a spawned session action. An explicit idle_timeout always wins. Otherwise an auto_cleanup="always" session gets defaultAutoCleanupIdle so it reaps itself promptly. "on_success" is deliberately not auto-idled: an idle-stop is a non-zero (SIGTERM) exit that "on_success" would not clean up, so idling it would just leave stopped clutter — the very thing auto_cleanup avoids. 0 means "use the agent default".

func (ActionConfig) TimeoutDuration added in v0.67.0

func (a ActionConfig) TimeoutDuration() time.Duration

TimeoutDuration returns a command action's timeout, defaulting to 5m.

type Agent

type Agent struct {
	Command      string            `json:"command"                 toml:"command"`
	Args         []string          `json:"args,omitempty"          toml:"args"`
	ResumeArgs   []string          `json:"resume_args,omitempty"   toml:"resume_args"`
	ForkArgs     []string          `json:"fork_args,omitempty"     toml:"fork_args"`
	Env          map[string]string `json:"env,omitempty"           toml:"env"`
	IdleTimeout  string            `json:"idle_timeout,omitempty"  toml:"idle_timeout"`
	InjectPrompt *bool             `json:"inject_prompt,omitempty" toml:"inject_prompt"`
	// PromptInjection selects HOW graith delivers its operating prompt to this
	// agent (append_system_prompt / cursor_rules / developer_instructions /
	// none). It is distinct from InjectPrompt, which is the on/off switch. An
	// empty value falls back to name-based detection so the built-in claude,
	// cursor, and codex agents work without explicit config; a custom agent
	// must set this to receive a prompt at all. Validated in Config.Validate.
	// See issue #1232.
	PromptInjection   string                     `json:"prompt_injection,omitempty"    toml:"prompt_injection"`
	PreTrustWorkspace *bool                      `json:"pre_trust_workspace,omitempty" toml:"pre_trust_workspace"`
	Sandbox           SandboxConfig              `json:"sandbox"                       toml:"sandbox"`
	MCPServers        map[string]MCPServerConfig `json:"mcp_servers,omitempty"         toml:"mcp_servers"`
	ValidateModel     string                     `json:"validate_model,omitempty"      toml:"validate_model"`
	// InterruptCount is how many times the interrupt byte (Ctrl-C, 0x03) is sent
	// to interrupt this agent, and InterruptDelayMs is the pause in milliseconds
	// between successive sends. Some agent TUIs ignore a single Ctrl-C and need
	// two rapid presses to actually interrupt (Claude's TUI wants ~200ms apart),
	// so both are configurable per agent. Unset means the built-in defaults
	// (count 1, delay 0). See issue #620.
	InterruptCount   *int `json:"interrupt_count,omitempty"    toml:"interrupt_count"`
	InterruptDelayMs *int `json:"interrupt_delay_ms,omitempty" toml:"interrupt_delay_ms"`
	// HeadlessCapable marks an agent as supporting headless stream-json mode
	// (issue #1075). Unset means not capable — only agents explicitly flagged
	// (Claude Code in v1) may run headless, so a --headless request against an
	// unsupported agent fails closed rather than silently downgrading.
	HeadlessCapable *bool `json:"headless_capable,omitempty" toml:"headless_capable"`
	// AddDirArgs is the flag template graith uses to grant the agent access to an
	// additional directory — each included repo's co-located worktree. It is
	// expanded once per directory with {dir} bound to that path (see
	// AddDirArgsFor). An empty AddDirArgs means the agent's CLI has no such flag,
	// so its included worktrees are exposed only via the GRAITH_INCLUDE_*_PATH env
	// vars; this replaces the former hard-coded agentSupportsAddDir allowlist so a
	// custom agent can opt in from config alone (issue #1236). Built-in
	// claude/codex/cursor set ["--add-dir", "{dir}"].
	AddDirArgs []string `json:"add_dir_args,omitempty" toml:"add_dir_args"`
	// HeadlessArgs is the argv prefix graith prepends when launching this agent in
	// headless stream-json mode (issue #1075); the agent's own template-expanded
	// args follow it. Only consulted for a headless session. Moving it here (from
	// the former hard-coded headlessArgs) lets a custom headless_capable agent
	// define its own control-channel flags (issue #1236). Built-in claude sets the
	// `-p --output-format stream-json …` flags.
	HeadlessArgs []string `json:"headless_args,omitempty" toml:"headless_args"`
	// OptionArgs are conditional argv groups appended after the base args on every
	// launch (create/resume/fork). Each group's Args are template-expanded and
	// appended only when its When template variable resolves non-empty, so an
	// unset option leaves the agent's own default untouched — e.g. codex's
	// ["--model", "{model}"] gated on `when = "model"`. This moves the formerly
	// hard-coded codex adapter (model / profile / reasoning-effort / service-tier
	// / search / approval flags) into config so custom agents can define their own
	// conditional flags (issue #1236).
	OptionArgs []AgentOptionArg `json:"option_args,omitempty" toml:"option_args"`
}

func (Agent) AddDirArgsFor added in v0.69.1

func (a Agent) AddDirArgsFor(base TemplateVars, dirs []string) ([]string, error)

AddDirArgsFor builds the add-directory flags granting the agent access to each of dirs, expanding a.AddDirArgs once per directory with {dir} bound to it (the rest of base is carried through so a template may also reference the usual vars). Empty AddDirArgs — or no directories — yields nil, so an agent whose CLI has no add-dir flag never has an unknown flag injected. Empty directory entries are skipped defensively.

func (Agent) HeadlessCapableEnabled added in v0.67.8

func (a Agent) HeadlessCapableEnabled() bool

HeadlessCapableEnabled reports whether this agent may run in headless stream-json mode. Defaults to false when unset.

func (Agent) IdleTimeoutDuration

func (a Agent) IdleTimeoutDuration() time.Duration

func (Agent) InterruptCountValue added in v0.66.2

func (a Agent) InterruptCountValue() int

InterruptCountValue returns how many times the interrupt byte (Ctrl-C, 0x03) should be sent to interrupt this agent. Defaults to 1 when unset; a value below 1 is clamped to 1 so an interrupt always sends at least once.

func (Agent) InterruptDelay added in v0.66.2

func (a Agent) InterruptDelay() time.Duration

InterruptDelay returns the pause between successive interrupt bytes. Defaults to 0 (send back-to-back) when unset; a negative value is treated as 0.

func (Agent) OptionArgsFor added in v0.69.1

func (a Agent) OptionArgsFor(vars TemplateVars) ([]string, error)

OptionArgsFor expands the agent's conditional option-arg groups against vars, appending each group only when its When gate resolves non-empty (an empty When always emits). Returns nil when no group fires, so it is safe to append unconditionally on every launch path. This is the config-driven replacement for the hard-coded codex flag adapter (issue #1236).

func (Agent) PreTrustWorkspaceEnabled added in v0.48.0

func (a Agent) PreTrustWorkspaceEnabled() bool

func (Agent) PromptInjectionEnabled added in v0.32.0

func (a Agent) PromptInjectionEnabled() bool

type AgentOptionArg added in v0.69.1

type AgentOptionArg struct {
	// When names the template variable that gates this group: the args are
	// emitted only when the variable resolves to a non-empty value ("true" for a
	// boolean such as web_search). An empty When emits the group unconditionally.
	When string `json:"when,omitempty" toml:"when"`
	// Args are the argv templates emitted when the gate passes. They are expanded
	// with the same TemplateVars as the base args plus the option variables.
	Args []string `json:"args" toml:"args"`
}

AgentOptionArg is one conditional argv group for an agent (see Agent.OptionArgs).

type Approvals added in v0.13.0

type Approvals struct {
	// Enabled controls whether the PreToolUse approve-request gating hook is
	// installed. nil (unset) means disabled: the status/lifecycle hooks are
	// still installed but the approval gate is not, because unattended agents
	// otherwise see their own tool calls as human-rejected and the OS sandbox
	// is the intended guardrail. Set to true to opt back into human approval
	// gating.
	Enabled *bool `toml:"enabled"`
	// Backend selects who makes the automated decision: "" (none — always
	// prompt the human), "command"/"external" (delegate to a command over
	// graith's JSON contract), "localmost" (the real localmost binary over its
	// native protocol), or "builtin" (graith's built-in localmost-compatible
	// engine). It is the canonical selector; Mode is the deprecated predecessor.
	Backend string           `toml:"backend"`
	Mode    string           `toml:"mode"`
	AutoPop bool             `toml:"auto_pop"`
	Timeout string           `toml:"timeout"`
	Command string           `toml:"command"`
	Builtin ApprovalsBuiltin `toml:"builtin"`

	// CommandTimeout bounds a single external "command"/"external" backend
	// invocation; LocalmostTimeout bounds a single "localmost" binary check.
	// Both default to defaultBackendExecTimeout (5s) when unset (see
	// CommandTimeoutDuration/LocalmostTimeoutDuration). Each must be positive,
	// no larger than maxBackendExecTimeout, and strictly shorter than the
	// enclosing human/headless approval Timeout so a hung backend cannot outlive
	// the deadline that encloses it — a class of bug that previously caused
	// approval-behaviour glitches (see #244). Validate enforces this hierarchy.
	CommandTimeout   string `toml:"command_timeout"`
	LocalmostTimeout string `toml:"localmost_timeout"`
}

func (Approvals) BackendExecTimeout added in v0.69.1

func (a Approvals) BackendExecTimeout(backend string) (time.Duration, bool)

BackendExecTimeout returns the effective execution timeout for a resolved backend name and whether that backend runs a bounded subprocess at all. Only the command/external and localmost backends spawn a child process; the others (prompt/builtin/auto) decide in-process and have no execution timeout.

func (Approvals) CommandTimeoutDuration added in v0.69.1

func (a Approvals) CommandTimeoutDuration() time.Duration

CommandTimeoutDuration is the effective execution timeout for the command/external backend, falling back to defaultBackendExecTimeout when unset or unparseable. Validate rejects a set-but-invalid value up front, so a fallback here only happens for an unset field.

func (Approvals) HookEnabled added in v0.65.0

func (a Approvals) HookEnabled() bool

HookEnabled reports whether the approve-request PreToolUse hook should be installed. Defaults to false when unset — approval gating is opt-in.

func (Approvals) LocalmostTimeoutDuration added in v0.69.1

func (a Approvals) LocalmostTimeoutDuration() time.Duration

LocalmostTimeoutDuration is the effective execution timeout for the localmost backend, falling back to defaultBackendExecTimeout when unset or unparseable.

func (Approvals) ResolveBackend added in v0.64.4

func (a Approvals) ResolveBackend() (backend, deprecation string, err error)

ResolveBackend resolves the effective approvals backend, applying back-compat for the deprecated Mode field. It returns the backend name, a non-empty deprecation message when a legacy Mode value was used (callers log it once), and an error for an unknown backend or a conflicting Mode+Backend pair.

Resolution order:

  1. If Backend is set, use it. If a legacy Mode is ALSO set and maps to a different backend, that is a hard error (refuse to guess intent).
  2. Else if Mode is one of command/external/localmost, map it to the "command" backend (historical behaviour) and return a deprecation message. A Mode with no Backend is always a warning, never an error.
  3. Else, the "prompt" backend (no automation).

func (Approvals) TimeoutDuration added in v0.13.0

func (a Approvals) TimeoutDuration() time.Duration

func (Approvals) Validate added in v0.64.5

func (a Approvals) Validate() error

Validate checks the [approvals] config for static contradictions that would otherwise only surface as an opaque fail-closed session crash at create time (see #740). It rejects an unknown or conflicting backend/mode (via ResolveBackend) and a command key set for a resolved backend that ignores it. Backend *availability* (command present, localmost binary on PATH, builtin config loadable) is still deferred to session-create by the daemon.

type ApprovalsBuiltin added in v0.64.4

type ApprovalsBuiltin struct {
	// Config is the path to a localmost-format config.json (allow/deny rules).
	Config string `toml:"config"`

	// Allow and Deny are the inline allow/deny rulesets. Each element is either
	// a bare rule string ("@arg @*") or a table with per-rule keys
	// (rule/unless/redirect/pipe). They are decoded as []any so both TOML forms
	// — an array of strings and an array of tables ([[approvals.builtin.allow]])
	// — are accepted, then converted to the localmost schema (see InlineJSON).
	Allow []any `toml:"allow"`
	Deny  []any `toml:"deny"`

	// AllowSafeXargs and AskNoninteractive mirror the localmost top-level flags.
	// nil means unset (the engine's default of true applies).
	AllowSafeXargs    *bool `toml:"allowSafeXargs"`
	AskNoninteractive *bool `toml:"askNoninteractive"`
}

ApprovalsBuiltin configures the built-in localmost-compatible engine. Rules can be supplied either as a path to an external localmost-format config.json (Config), or inline in config.toml via Allow/Deny/AllowSafeXargs/ AskNoninteractive. The two forms are mutually exclusive (see Approvals.Validate).

func (ApprovalsBuiltin) HasInline added in v0.66.0

func (b ApprovalsBuiltin) HasInline() bool

HasInline reports whether any inline ruleset field is set. When true, the rules are read from config.toml rather than an external Config file. An empty array (allow = []) defines no rules and does not count as inline, so it does not spuriously conflict with an external Config path.

func (ApprovalsBuiltin) InlineJSON added in v0.66.0

func (b ApprovalsBuiltin) InlineJSON() ([]byte, error)

InlineJSON renders the inline ruleset as localmost-format config.json bytes, so the existing (tested) localmost parser can compile it. The TOML keys map 1:1 to the localmost JSON schema (allow/deny/allowSafeXargs/askNoninteractive, and per-rule rule/unless/redirect/pipe), so a plain JSON re-encode suffices.

type CodexOptions added in v0.68.8

type CodexOptions struct {
	Profile         string `json:"profile,omitempty"`
	ReasoningEffort string `json:"reasoning_effort,omitempty"`
	ServiceTier     string `json:"service_tier,omitempty"`
	WebSearch       bool   `json:"web_search,omitempty"`
	ApprovalPolicy  string `json:"approval_policy,omitempty"`
}

CodexOptions holds typed per-session options for the Codex CLI (issue #1186). Each maps to a Codex flag or `-c` config override and is emitted only when set, so an unset field leaves Codex's own default untouched. The session model is tracked separately (SessionState.Model / CreateOpts.Model) and is not repeated here. These are Codex-specific: setting any against a non-codex agent is an error rather than a silent no-op. Reasoning effort and service tier are passed as `-c model_reasoning_effort=…` / `-c service_tier=…` because Codex has no dedicated flag for them; profile, web search, and approval policy have flags.

func (CodexOptions) IsZero added in v0.68.8

func (o CodexOptions) IsZero() bool

IsZero reports whether no Codex option is set.

type CompletionConfig added in v0.69.2

type CompletionConfig struct {
	Event   string `toml:"event"`
	Session string `toml:"session"`
}

CompletionConfig is the todo-derived scenario-completion source. It is only valid on a scenario-embedded trigger. Session optionally names the scenario member whose worktree supplies execution/mirror context; command and session actions require it. Event defaults to "complete" when omitted (the only v1 event).

type Config

type Config struct {
	DefaultAgent     string             `toml:"default_agent"`
	GitHubUsername   string             `toml:"github_username"`
	BranchPrefix     string             `toml:"branch_prefix"`
	DataDir          string             `toml:"data_dir"`
	FetchOnCreate    bool               `toml:"fetch_on_create"`
	AgentPrompt      string             `toml:"agent_prompt"`
	AllowedRepoPaths []string           `toml:"allowed_repo_paths"`
	Repos            []RepoConfig       `toml:"repos"`
	StatusBar        StatusBar          `toml:"status_bar"`
	Keybindings      Keybindings        `toml:"keybindings"`
	Notifications    Notifications      `toml:"notifications"`
	Messages         Messages           `toml:"messages"`
	Delete           Delete             `toml:"delete"`
	GC               GCConfig           `toml:"gc"`
	Todo             TodoConfig         `toml:"todo"`
	Sandbox          SandboxConfig      `toml:"sandbox"`
	Approvals        Approvals          `toml:"approvals"`
	Status           StatusConfig       `toml:"status"`
	GitPull          GitPullConfig      `toml:"git_pull"`
	Launch           LaunchConfig       `toml:"launch"`
	PRWatch          PRWatchConfig      `toml:"pr_watch"`
	MCPServers       []MCPServerConfig  `toml:"mcp_servers"`
	Overlay          Overlay            `toml:"overlay"`
	Orchestrator     OrchestratorConfig `toml:"orchestrator"`
	Remote           RemoteConfig       `toml:"remote"`
	Input            InputConfig        `toml:"input"`
	Agents           map[string]Agent   `toml:"agents"`
	Triggers         []TriggerConfig    `toml:"trigger"`          // [[trigger]] array
	TriggersRuntime  TriggersRuntime    `toml:"triggers"`         // [triggers] table (daemon-wide settings)
	Headless         HeadlessConfig     `toml:"headless"`         // [headless] table (issue #1075)
	Updates          UpdatesConfig      `toml:"updates"`          // [updates] table (issue #1253)
	Detection        DetectionConfig    `toml:"detection"`        // [detection] table (issue #1241)
	ConfigReload     ConfigReload       `toml:"config"`           // [config] table (issue #1237)
	Tools            ToolsConfig        `toml:"tools"`            // [tools] table (issue #1238)
	Git              GitConfig          `toml:"git"`              // [git] table (issue #1238)
	Connection       ConnectionConfig   `toml:"connection"`       // [connection] table (issue #1242)
	TokenAccounting  TokenAccounting    `toml:"token_accounting"` // [token_accounting] table (issue #1244)
	ResourceMonitor  ResourceMonitor    `toml:"resource_monitor"` // [resource_monitor] table (issue #1244)
	Migration        MigrationConfig    `toml:"migration"`        // [migration] table (issue #1250)
	Transcript       TranscriptConfig   `toml:"transcript"`       // [transcript] table (issue #1250)
	Limits           LimitsConfig       `toml:"limits"`           // [limits] table (issue #1252)
	Lifecycle        LifecycleConfig    `toml:"lifecycle"`        // [lifecycle] table (issue #1243)
	Terminal         TerminalConfig     `toml:"terminal"`         // [terminal] table (issue #1254)

	// Warnings collects non-fatal configuration problems detected at load time
	// (e.g. conflicting keybindings). They are surfaced to the user but do not
	// prevent startup. Not serialised. See issue #1233.
	Warnings []string `toml:"-"`

	// SourceDir is the absolute directory containing the config.toml this Config
	// was loaded from. It anchors relative paths in the config (currently the
	// [tools] executable overrides) so they mean the same file regardless of the
	// process working directory or a later exec.Cmd.Dir. Empty for an in-memory
	// Default() with no source file. Not serialised. See issue #1293.
	SourceDir string `toml:"-"`
}

func Default

func Default() *Config

func Load

func Load(path string) (*Config, error)

func LoadOrDefault

func LoadOrDefault(path string) (*Config, error)

func RedactSecrets added in v0.68.5

func RedactSecrets(cfg *Config) *Config

RedactSecrets returns a copy of cfg with secret-bearing values masked: the per-server and per-agent `env` maps, whose values routinely hold tokens and API keys inline in config.toml. Map keys are preserved (so the shape stays visible); only the values are replaced with RedactedMask. cfg is not mutated.

The daemon renders this — not the raw config — over the control protocol, so a remote paired human, or a local session reading via the socket, sees the configuration structure without its secrets. `gr config show`/`diff` read the file directly (not through the daemon) and are deliberately unaffected.

func (*Config) AvailableRepoPaths added in v0.66.13

func (c *Config) AvailableRepoPaths() []string

AvailableRepoPaths returns the repo paths the orchestrator may use, combining the allowed_repo_paths list and the [[repos]] entries with ~ expanded, in config order and de-duplicated. It returns nil when none are configured.

func (*Config) FindRepo added in v0.18.0

func (c *Config) FindRepo(repoPath string) (RepoConfig, bool)

func (*Config) OrchestratorSandboxMerged added in v0.46.0

func (c *Config) OrchestratorSandboxMerged(agentName string) SandboxConfig

func (*Config) RepoPathAllowed added in v0.11.0

func (c *Config) RepoPathAllowed(repoPath string) bool

func (*Config) Validate added in v0.19.0

func (c *Config) Validate() error

type ConfigReload added in v0.69.1

type ConfigReload struct {
	// ReloadDebounce is the quiet period the file watcher waits after the last
	// write before reloading. Empty, unparseable, or non-positive uses the
	// default (ConfigReloadDebounceDefault).
	ReloadDebounce string `toml:"reload_debounce"`
}

ConfigReload is the config block: settings governing how graith handles its own configuration file. Currently just the hot-reload debounce, moved out of a bare literal in the watcher so `gr config show` reflects it (issue #1237).

func (ConfigReload) ReloadDebounceDuration added in v0.69.1

func (c ConfigReload) ReloadDebounceDuration() time.Duration

ReloadDebounceDuration resolves the config-reload debounce. Empty, unparseable, or non-positive falls back to the default so a typo never busy-loops the watcher (Validate rejects a set-but-invalid value at load; this is the runtime fail-safe).

type ConnectionConfig added in v0.69.1

type ConnectionConfig struct {
	// DialTimeout bounds a single Unix-socket dial to the local daemon
	// (default "500ms").
	DialTimeout string `toml:"dial_timeout"`
	// HandshakeTimeout bounds the local-daemon handshake exchange, so a stale
	// or wedged socket can't hang a command forever (default "5s").
	HandshakeTimeout string `toml:"handshake_timeout"`
	// StartTimeout is the aggregate bound while EnsureDaemon waits for a freshly
	// spawned daemon to begin answering handshakes (default "5s"). Individual
	// dial/handshake policies are capped at the time remaining.
	StartTimeout string `toml:"start_timeout"`
	// StartPollInterval is how often EnsureDaemon re-probes the socket while
	// waiting for a spawned daemon to come up (default "50ms").
	StartPollInterval string `toml:"start_poll_interval"`
	// ReconnectTimeout bounds the attach disconnect-recovery retry before the
	// client gives up reattaching (default "10s").
	ReconnectTimeout string `toml:"reconnect_timeout"`
	// ReconnectInterval is how often the attach recovery loop re-probes the
	// daemon while reconnecting (default "250ms").
	ReconnectInterval string `toml:"reconnect_interval"`
	// RemoteDialTimeout bounds the TCP dial to a paired remote daemon
	// (default "10s").
	RemoteDialTimeout string `toml:"remote_dial_timeout"`
	// RemoteHandshakeTimeout bounds the remote handshake plus
	// proof-of-possession exchange (default "15s").
	RemoteHandshakeTimeout string `toml:"remote_handshake_timeout"`
	// RemotePairingTimeout bounds how long the CLI waits for the remote human to
	// approve `gr pair`, and should sit just past the daemon's pending-pairing
	// TTL (default "11m").
	RemotePairingTimeout string `toml:"remote_pairing_timeout"`
}

ConnectionConfig is the [connection] block tuning the deadlines and retry cadence the stateless `gr` client applies when talking to a daemon (issue #1242). Previously these were hard-coded (local dial 500ms, handshake/start 5s, startup re-probe 50ms, attach reconnect 10s/250ms, remote dial/handshake 10s/15s, remote pairing wait 11m). Slow machines, high-latency links, and remote daemons on constrained networks can legitimately exceed the built-in bounds, so each is overridable. Every value is a duration; an empty, unparseable, or non-positive entry keeps the built-in default (a bad value is rejected at config load by Validate). These are read once at CLI startup and installed into the client, so a change takes effect on the next `gr` invocation.

func (ConnectionConfig) DialTimeoutDuration added in v0.69.1

func (c ConnectionConfig) DialTimeoutDuration() time.Duration

DialTimeoutDuration returns the local-daemon dial timeout, or the default when unset, unparseable, or non-positive (a zero/negative timeout would abort the dial immediately).

func (ConnectionConfig) HandshakeTimeoutDuration added in v0.69.1

func (c ConnectionConfig) HandshakeTimeoutDuration() time.Duration

HandshakeTimeoutDuration returns the local-daemon handshake timeout, or the default when unset, unparseable, or non-positive.

func (ConnectionConfig) ReconnectIntervalDuration added in v0.69.1

func (c ConnectionConfig) ReconnectIntervalDuration() time.Duration

ReconnectIntervalDuration returns the attach reconnect re-probe interval, or the default when unset, unparseable, or non-positive (a zero interval would busy-loop).

func (ConnectionConfig) ReconnectTimeoutDuration added in v0.69.1

func (c ConnectionConfig) ReconnectTimeoutDuration() time.Duration

ReconnectTimeoutDuration returns the attach reconnect deadline, or the default when unset, unparseable, or non-positive.

func (ConnectionConfig) RemoteDialTimeoutDuration added in v0.69.1

func (c ConnectionConfig) RemoteDialTimeoutDuration() time.Duration

RemoteDialTimeoutDuration returns the remote TCP dial timeout, or the default when unset, unparseable, or non-positive.

func (ConnectionConfig) RemoteHandshakeTimeoutDuration added in v0.69.1

func (c ConnectionConfig) RemoteHandshakeTimeoutDuration() time.Duration

RemoteHandshakeTimeoutDuration returns the remote handshake/PoP timeout, or the default when unset, unparseable, or non-positive.

func (ConnectionConfig) RemotePairingTimeoutDuration added in v0.69.1

func (c ConnectionConfig) RemotePairingTimeoutDuration() time.Duration

RemotePairingTimeoutDuration returns the remote pairing-approval wait, or the default when unset, unparseable, or non-positive.

func (ConnectionConfig) StartPollIntervalDuration added in v0.69.1

func (c ConnectionConfig) StartPollIntervalDuration() time.Duration

StartPollIntervalDuration returns the daemon-startup re-probe interval, or the default when unset, unparseable, or non-positive (a zero interval would busy-loop).

func (ConnectionConfig) StartTimeoutDuration added in v0.69.1

func (c ConnectionConfig) StartTimeoutDuration() time.Duration

StartTimeoutDuration returns the daemon-startup wait, or the default when unset, unparseable, or non-positive.

type Delete added in v0.66.16

type Delete struct {
	Retention         string `toml:"retention"`
	PurgeStartupDelay string `toml:"purge_startup_delay"`
	PurgeInterval     string `toml:"purge_interval"`
}

Delete configures the soft-delete behaviour of `gr delete`. When retention is a positive duration, `gr delete` marks a session deleted and keeps its worktree/state for the window; the daemon purges it after the window elapses. A retention of "0" disables soft delete: `gr delete` is then rejected (with a message pointing at `gr purge`), since delete must never destroy — `gr purge` remains the way to hard-delete immediately.

PurgeStartupDelay and PurgeInterval tune ONLY the sweep cadence, never whether a session is recoverable: a session is purged only once its frozen ExpiresAt (DeletedAt + retention) has passed, so no timing value can turn soft delete into an immediate hard delete.

func (Delete) PurgeIntervalDuration added in v0.69.1

func (d Delete) PurgeIntervalDuration() time.Duration

PurgeIntervalDuration resolves the steady-state interval between purge sweeps. Unset, unparseable, or non-positive values fall back to the default.

func (Delete) PurgeStartupDelayDuration added in v0.69.1

func (d Delete) PurgeStartupDelayDuration() time.Duration

PurgeStartupDelayDuration resolves the delay before the first purge sweep. Unset, unparseable, or non-positive values fall back to the default so a typo never silently changes startup behaviour (Validate rejects a bad value at load; this is the runtime fail-safe).

func (Delete) RetentionDuration added in v0.66.16

func (d Delete) RetentionDuration() time.Duration

RetentionDuration resolves the configured soft-delete retention window. An unset value defaults to DefaultDeleteRetention (24h); "0" (or any zero duration) disables soft delete. An unparseable value falls back to the default so a typo never silently turns off recovery.

type DeliverConfig added in v0.67.0

type DeliverConfig struct {
	Inbox    string `toml:"inbox"`    // session name, "orchestrator", or a template like "{session_name}"
	Topic    string `toml:"topic"`    // pub/sub topic
	Store    string `toml:"store"`    // store key (prefix "shared:" for the shared store)
	Wake     bool   `toml:"wake"`     // resume a non-orchestrator stopped inbox target
	Required bool   `toml:"required"` // delivery failure fails a completion action and gates on_success cleanup
}

DeliverConfig routes action output. All fields are templated at fire time.

type DetectionConfig added in v0.69.1

type DetectionConfig struct {
	// ScanInterval is how often the detection loop scans PTY scrollback to
	// classify low-risk agent status (active/ready). Empty uses the default
	// (DetectionScanIntervalDefault).
	ScanInterval string `toml:"scan_interval"`
	// FetchInterval is how often the detection loop refreshes remote tracking
	// refs (`git fetch`) so the diverged-from-base count stays fresh. Empty uses
	// the default (DetectionFetchIntervalDefault).
	FetchInterval string `toml:"fetch_interval"`
	// FetchTimeout bounds a single per-repo `git fetch` so a slow or hung remote
	// can't stall the fetch pass for other sessions. Empty uses the default
	// (DetectionFetchTimeoutDefault).
	FetchTimeout string `toml:"fetch_timeout"`
	// SilentThreshold is how long a running session's PTY may produce zero
	// output before the daemon warns it is silent (issue #1087). Empty uses the
	// default (DetectionSilentThresholdDefault).
	SilentThreshold string `toml:"silent_threshold"`
	// AdoptedGrace is the window after daemon-upgrade PTY adoption during which
	// an unknown detection result falls back to the previous status instead of
	// clobbering it. Empty uses the default (DetectionAdoptedGraceDefault).
	AdoptedGrace string `toml:"adopted_grace"`
	// RecentOutputWindow is the age below which recent PTY output alone implies
	// the agent is active when pattern matching is inconclusive. Empty uses the
	// default (DetectionRecentOutputWindowDefault).
	RecentOutputWindow string `toml:"recent_output_window"`
	// HookStartWindow is how long a SessionStart hook report stays authoritative
	// over PTY scraping. Empty uses the default (DetectionHookStartWindowDefault).
	HookStartWindow string `toml:"hook_start_window"`
	// HookActivityWindow is how long a tool-use hook report (UserPromptSubmit,
	// PreToolUse, PostToolUse) stays authoritative. Empty uses the default
	// (DetectionHookActivityWindowDefault).
	HookActivityWindow string `toml:"hook_activity_window"`
	// HookTerminalWindow is how long a terminal hook report (ready/approval:
	// Stop, idle_prompt, permission_prompt, PermissionRequest) stays
	// authoritative. Empty uses the default (DetectionHookTerminalWindowDefault).
	HookTerminalWindow string `toml:"hook_terminal_window"`
}

DetectionConfig is the [detection] block gathering the agent-detection and status-classification timing policy that was previously spread as fixed constants across the daemon and detector packages (issue #1241). Every field is optional: an empty or unparseable value falls back to the matching default constant, preserving the historical behaviour.

func (DetectionConfig) AdoptedGraceDuration added in v0.69.1

func (d DetectionConfig) AdoptedGraceDuration() time.Duration

AdoptedGraceDuration returns the adopted-session fallback window, or the default when unset or unparseable. A "0" disables the fallback.

func (DetectionConfig) FetchIntervalDuration added in v0.69.1

func (d DetectionConfig) FetchIntervalDuration() time.Duration

FetchIntervalDuration returns the remote-fetch cadence, or the default when unset, unparseable, or non-positive.

func (DetectionConfig) FetchTimeoutDuration added in v0.69.1

func (d DetectionConfig) FetchTimeoutDuration() time.Duration

FetchTimeoutDuration returns the per-repo fetch timeout, or the default when unset, unparseable, or non-positive.

func (DetectionConfig) HookActivityWindowDuration added in v0.69.1

func (d DetectionConfig) HookActivityWindowDuration() time.Duration

HookActivityWindowDuration returns the tool-use hook-authority window, or the default when unset, unparseable, or non-positive.

func (DetectionConfig) HookStartWindowDuration added in v0.69.1

func (d DetectionConfig) HookStartWindowDuration() time.Duration

HookStartWindowDuration returns the SessionStart hook-authority window, or the default when unset, unparseable, or non-positive.

func (DetectionConfig) HookTerminalWindowDuration added in v0.69.1

func (d DetectionConfig) HookTerminalWindowDuration() time.Duration

HookTerminalWindowDuration returns the ready/approval hook-authority window, or the default when unset, unparseable, or non-positive.

func (DetectionConfig) RecentOutputWindowDuration added in v0.69.1

func (d DetectionConfig) RecentOutputWindowDuration() time.Duration

RecentOutputWindowDuration returns the recent-output-implies-active window, or the default when unset or unparseable. A "0" disables the fallback.

func (DetectionConfig) ScanIntervalDuration added in v0.69.1

func (d DetectionConfig) ScanIntervalDuration() time.Duration

ScanIntervalDuration returns the PTY scan cadence, or the default when unset, unparseable, or non-positive (a zero/negative scan interval would busy-loop).

func (DetectionConfig) SilentThresholdDuration added in v0.69.1

func (d DetectionConfig) SilentThresholdDuration() time.Duration

SilentThresholdDuration returns the zero-output warning window. Empty or unparseable uses the default; a non-positive value keeps the default because a session is never past a "zero" threshold in a meaningful way.

type GCConfig added in v0.69.1

type GCConfig struct {
	OrphanMinAge string `toml:"orphan_min_age"`
}

GCConfig is the [gc] block. It tunes orphan garbage collection — the sweep (via `gr gc`) that reclaims worktree and scratch directories left behind by sessions no longer in state.

func (GCConfig) OrphanMinAgeDuration added in v0.69.1

func (g GCConfig) OrphanMinAgeDuration() time.Duration

OrphanMinAgeDuration resolves the orphan minimum age. Unset or unparseable falls back to the default; a negative value also falls back (a bad value must not widen GC to newly-created directories). "0" is honoured: an operator who explicitly opts out of the age floor gets immediate GC eligibility.

type GCXConfig added in v0.69.2

type GCXConfig struct {
	Event          string   `toml:"event"`           // oncall_alert_group (v1; default)
	Context        string   `toml:"context"`         // gcx context (required; credentials remain owned by gcx)
	Every          string   `toml:"every"`           // poll cadence; default 1m
	Timeout        string   `toml:"timeout"`         // timeout for each gcx invocation; default 30s
	OnCallUserID   string   `toml:"oncall_user_id"`  // stable human user PK; paired with ScheduleIDs
	ScheduleIDs    []string `toml:"schedule_ids"`    // schedules used for the current-on-call gate
	TeamIDs        []string `toml:"team_ids"`        // optional alert-group team filters
	IntegrationIDs []string `toml:"integration_ids"` // optional alert-group integration filters
	States         []string `toml:"states"`          // firing|acknowledged|resolved|silenced; default firing
	MaxAge         string   `toml:"max_age"`         // alert lookback and seen-ID retention; default 24h
	Limit          int      `toml:"limit"`           // result cap; reaching it fails closed; default 100
}

GCXConfig is the Grafana Cloud event source. V1 polls OnCall alert groups through an existing gcx context and can gate delivery on a pinned human being currently present in one of the selected OnCall schedules.

func (GCXConfig) EventOr added in v0.69.2

func (g GCXConfig) EventOr() string

EventOr returns the selected event kind, defaulting to the only v1 kind.

func (GCXConfig) EveryDuration added in v0.69.2

func (g GCXConfig) EveryDuration() time.Duration

EveryDuration returns the poll cadence, defaulting to 1m.

func (GCXConfig) LimitOr added in v0.69.2

func (g GCXConfig) LimitOr() int

LimitOr returns the alert-group result cap, defaulting to 100.

func (GCXConfig) MaxAgeDuration added in v0.69.2

func (g GCXConfig) MaxAgeDuration() time.Duration

MaxAgeDuration returns the alert lookback/cursor retention, defaulting to 24h.

func (GCXConfig) StatesOr added in v0.69.2

func (g GCXConfig) StatesOr() []string

StatesOr returns a fresh copy of the configured states, defaulting to firing.

func (GCXConfig) TimeoutDuration added in v0.69.2

func (g GCXConfig) TimeoutDuration() time.Duration

TimeoutDuration returns the per-gcx-command timeout, defaulting to 30s.

type GitConfig added in v0.69.1

type GitConfig struct {
	// FetchTimeout bounds a single `git fetch` (default "2m").
	FetchTimeout string `toml:"fetch_timeout"`
	// MergeTimeout bounds a single fast-forward merge in the git-pull loop
	// (default "2m").
	MergeTimeout string `toml:"merge_timeout"`
	// UsernameTimeout bounds GitHub-username discovery, which may invoke `gh`
	// (default "15s").
	UsernameTimeout string `toml:"username_timeout"`
}

GitConfig is the [git] block tuning the timeouts graith applies to the git operations it runs during session lifecycle (issue #1238). Slower repositories, large fetches, and high-latency remotes can legitimately exceed the built-in 2m fetch / 2m merge / 15s username bounds. An empty field keeps the built-in default. Note this is distinct from [git_pull], which configures the background maintenance-pull loop.

func (GitConfig) FetchTimeoutDuration added in v0.69.1

func (g GitConfig) FetchTimeoutDuration() time.Duration

FetchTimeoutDuration returns the configured git-fetch timeout, defaulting to 2m when unset or unparseable (a bad value is rejected at load by Validate).

func (GitConfig) MergeTimeoutDuration added in v0.69.1

func (g GitConfig) MergeTimeoutDuration() time.Duration

MergeTimeoutDuration returns the configured merge timeout, defaulting to 2m.

func (GitConfig) UsernameTimeoutDuration added in v0.69.1

func (g GitConfig) UsernameTimeoutDuration() time.Duration

UsernameTimeoutDuration returns the configured username-discovery timeout, defaulting to 15s.

type GitPullConfig added in v0.42.0

type GitPullConfig struct {
	Enabled  bool   `toml:"enabled"`
	Interval string `toml:"interval"`
}

func (GitPullConfig) IntervalDuration added in v0.42.0

func (g GitPullConfig) IntervalDuration() time.Duration

type HeadlessConfig added in v0.67.8

type HeadlessConfig struct {
	Experimental bool `toml:"experimental"`
	Default      bool `toml:"default"`
	// MaxLineBytes bounds a single stream-json line read from the agent's stdout.
	// Large tool outputs or base64 images exceed the 64KiB default scanner token,
	// so the driver raises the cap. 0/negative uses HeadlessMaxLineBytesDefault.
	MaxLineBytes int `toml:"max_line_bytes"`
	// ControlTimeout bounds how long a synchronous control request waits for its
	// matching control_response before failing. Empty/unparseable/non-positive
	// uses HeadlessControlTimeoutDefault.
	ControlTimeout string `toml:"control_timeout"`
	// InterruptTimeout bounds the interrupt control round-trip; it is much shorter
	// than ControlTimeout because a caller interrupting an agent wants a prompt
	// fall-through to SIGINT. Empty/unparseable/non-positive uses
	// HeadlessInterruptTimeoutDefault.
	InterruptTimeout string `toml:"interrupt_timeout"`
	// PreviewBytes bounds how much scrollback tail the overlay preview and
	// screen_preview control message render. 0/negative uses
	// HeadlessPreviewBytesDefault.
	PreviewBytes int `toml:"preview_bytes"`
}

HeadlessConfig is the [headless] block gating headless stream-json sessions (issue #1075). Headless is inert unless Experimental is true — the control protocol it uses is an SDK-internal contract, so v1 is opt-in and experimental. Default, when Experimental is on, decides whether new sessions go headless without an explicit --headless.

The remaining fields make the headless driver's processing limits tunable (issue #1250). Each is optional: an empty/zero/non-positive value falls back to the matching default constant, preserving historical behaviour.

func (HeadlessConfig) ControlTimeoutDuration added in v0.69.1

func (h HeadlessConfig) ControlTimeoutDuration() time.Duration

ControlTimeoutDuration returns the control-request timeout, or the default when unset, unparseable, or non-positive.

func (HeadlessConfig) InterruptTimeoutDuration added in v0.69.1

func (h HeadlessConfig) InterruptTimeoutDuration() time.Duration

InterruptTimeoutDuration returns the interrupt round-trip timeout, or the default when unset, unparseable, or non-positive.

func (HeadlessConfig) MaxLineBytesOrDefault added in v0.69.1

func (h HeadlessConfig) MaxLineBytesOrDefault() int

MaxLineBytesOrDefault returns the stream-json line cap, or the default when unset or non-positive.

func (HeadlessConfig) PreviewBytesOrDefault added in v0.69.1

func (h HeadlessConfig) PreviewBytesOrDefault() int

PreviewBytesOrDefault returns the preview tail cap, or the default when unset or non-positive.

type InputConfig added in v0.66.16

type InputConfig struct {
	// DragArrowKeys enables touch/hold-and-drag arrow keys:
	// press-and-hold the left mouse button then drag to emit discrete arrow-key
	// presses to the focused pane. Off by default because it repurposes
	// left-drag (which terminals otherwise use for text selection). Mouse-wheel
	// scrolling is always passed through unchanged.
	DragArrowKeys bool `toml:"drag_arrow_keys"`
	// DragArrowThreshold is the number of cells of drag movement that produces
	// one arrow-key press. Values below 1 fall back to the default.
	DragArrowThreshold int `toml:"drag_arrow_threshold"`
}

InputConfig is the optional [input] block controlling terminal input gestures in the attach passthrough loop.

type Keybindings

type Keybindings struct {
	Prefix              string `toml:"prefix"`
	NewSession          string `toml:"new_session"`
	ForkSession         string `toml:"fork_session"`
	DeleteSession       string `toml:"delete_session"`
	Detach              string `toml:"detach"`
	SessionList         string `toml:"session_list"`
	NextSession         string `toml:"next_session"`
	PrevSession         string `toml:"prev_session"`
	LastSession         string `toml:"last_session"`
	ResumeSession       string `toml:"resume_session"`
	RenameSession       string `toml:"rename_session"`
	Search              string `toml:"search"`
	ScrollMode          string `toml:"scroll_mode"`
	Shell               string `toml:"shell"`
	OrchestratorSession string `toml:"orchestrator_session"`
	// Prefix-action commands issued from an attached session (prefix key then
	// one of these). Previously hard-coded as m/a/r (issue #1233).
	Messages       string `toml:"messages"`
	Approvals      string `toml:"approvals"`
	RestartSession string `toml:"restart_session"`
	// Overlay holds the keys used inside the full-screen terminal overlays
	// (dashboard, approval prompt, message viewer, scroll pager). See #1233.
	Overlay OverlayKeybindings `toml:"overlay"`
}

func (Keybindings) Conflicts added in v0.69.1

func (k Keybindings) Conflicts() []string

Conflicts reports keybinding collisions among the prefix-action commands — the keys pressed after the prefix while attached to a session. Two commands bound to the same key mean only the first (in the passthrough switch order) ever fires, so the config is almost certainly a mistake. Picker/overlay keys operate in a separate mode and may legitimately reuse a prefix-command key, so they are not compared here. Each returned string names one collision; an empty slice means no conflicts. The result feeds a warning, not an error, so a misconfiguration is surfaced without refusing to start (issue #1233).

type LaunchConfig added in v0.67.7

type LaunchConfig struct {
	// MaxConcurrent bounds how many agent spawns may be in their startup window
	// at once. Values < 1 fall back to the default (LaunchMaxConcurrentDefault).
	MaxConcurrent int `toml:"max_concurrent"`
	// StartupTimeout is how long a session may stay running with no output before
	// the startup watchdog kills and restarts it fresh. "0" disables the
	// watchdog; empty uses the default (LaunchStartupTimeoutDefault).
	StartupTimeout string `toml:"startup_timeout"`
	// SettleTimeout caps how long a launch holds its throttle slot waiting for
	// the session's first output before releasing it anyway. Empty uses the
	// default (LaunchSettleTimeoutDefault); "0" releases immediately after spawn.
	SettleTimeout string `toml:"settle_timeout"`
	// MaxRestarts caps how many consecutive startup-watchdog restarts a single
	// session may receive before it is marked errored instead of restarted again,
	// preventing a restart storm for a fundamentally-broken session (#1092). The
	// counter resets once the session produces output. Values < 1 fall back to the
	// default (LaunchMaxRestartsDefault). To turn the watchdog off entirely, set
	// startup_timeout to "0" rather than dropping this to zero.
	MaxRestarts int `toml:"max_restarts"`
	// WatchdogInterval is how often the startup watchdog scans for stuck sessions.
	// Empty, unparseable, or non-positive uses the default
	// (LaunchWatchdogIntervalDefault); a zero cadence would busy-loop. Read once
	// when the watchdog loop starts, so a change takes effect on the next daemon
	// (re)start.
	WatchdogInterval string `toml:"watchdog_interval"`
	// SlotPollInterval is how often a held throttle slot polls a freshly-spawned
	// session for its first output before releasing. Empty, unparseable, or
	// non-positive uses the default (LaunchSlotPollIntervalDefault); a zero cadence
	// would busy-loop.
	SlotPollInterval string `toml:"slot_poll_interval"`
}

LaunchConfig bounds concurrent agent-session startup and recovers sessions that stall during launch (issue #1092). Bursts of `gr new` otherwise let many heavyweight agent runtimes initialise at once, and the tail can stall for minutes or hang forever at ~9MB RSS (sandbox wrapper only, agent never loaded).

func (LaunchConfig) MaxConcurrentOrDefault added in v0.67.7

func (l LaunchConfig) MaxConcurrentOrDefault() int

MaxConcurrentOrDefault returns the configured concurrency, clamped to a sensible minimum. A non-positive value means "use the default".

func (LaunchConfig) MaxRestartsOrDefault added in v0.69.1

func (l LaunchConfig) MaxRestartsOrDefault() int

MaxRestartsOrDefault returns the stuck-launch restart budget, or the default when < 1 (mirrors MaxConcurrentOrDefault). The watchdog is disabled via startup_timeout = "0", not by zeroing this.

func (LaunchConfig) SettleTimeoutDuration added in v0.67.7

func (l LaunchConfig) SettleTimeoutDuration() time.Duration

SettleTimeoutDuration returns how long a slot waits for first output. Empty uses the default; an explicit "0" releases the slot as soon as the spawn returns.

func (LaunchConfig) SlotPollIntervalDuration added in v0.69.1

func (l LaunchConfig) SlotPollIntervalDuration() time.Duration

SlotPollIntervalDuration returns the settle poll cadence, or the default when unset, unparseable, or non-positive (a zero cadence would busy-loop).

func (LaunchConfig) StartupTimeoutDuration added in v0.67.7

func (l LaunchConfig) StartupTimeoutDuration() time.Duration

StartupTimeoutDuration returns the watchdog threshold. Empty uses the default; an explicit "0" (or any non-positive parse) disables the watchdog.

func (LaunchConfig) WatchdogIntervalDuration added in v0.69.1

func (l LaunchConfig) WatchdogIntervalDuration() time.Duration

WatchdogIntervalDuration returns the watchdog scan cadence, or the default when unset, unparseable, or non-positive (a zero cadence would busy-loop).

type LifecycleConfig added in v0.69.1

type LifecycleConfig struct {
	// ConvertSettleTimeout bounds how long ConvertToInteractive waits for an
	// interrupted headless process to settle and exit before escalating to
	// SIGTERM. Empty or non-positive uses the default (ConvertSettleTimeoutDefault).
	ConvertSettleTimeout string `toml:"convert_settle_timeout"`
	// ConvertKillTimeout bounds the SIGTERM step before the final SIGKILL. Empty
	// or non-positive uses the default (ConvertKillTimeoutDefault).
	ConvertKillTimeout string `toml:"convert_kill_timeout"`
	// ConvertForceKillTimeout bounds the final wait after SIGKILL so a process
	// whose Done() never closes can't stall the convert forever. Empty or
	// non-positive uses the default (ConvertForceKillTimeoutDefault).
	ConvertForceKillTimeout string `toml:"convert_force_kill_timeout"`
	// MassExitWindow is the rolling window over which many near-simultaneous
	// session exits are counted as a likely external signal (OOM killer/jetsam).
	// Empty or non-positive uses the default (MassExitWindowDefault).
	MassExitWindow string `toml:"mass_exit_window"`
	// MassExitThreshold is how many exits within MassExitWindow trigger the
	// mass-exit warning. Values < 1 fall back to the default (MassExitThresholdDefault).
	MassExitThreshold int `toml:"mass_exit_threshold"`
	// ProcessKillGrace is how long killProcessGroup waits after SIGTERM before
	// sending SIGKILL to a session's process group. Empty or non-positive uses the
	// default (ProcessKillGraceDefault).
	ProcessKillGrace string `toml:"process_kill_grace"`
	// AdoptedTimeout is the safety deadline the adopted-PTY babysit loop applies
	// when it cannot verify process identity by start time. Empty or non-positive
	// uses the default (AdoptedTimeoutDefault). Applies to sessions adopted after
	// the change (daemon upgrade).
	AdoptedTimeout string `toml:"adopted_timeout"`
	// AdoptedPollInterval is how often the adopted-PTY babysit loop polls for
	// process exit. Empty or non-positive uses the default
	// (AdoptedPollIntervalDefault); a zero cadence would busy-loop.
	AdoptedPollInterval string `toml:"adopted_poll_interval"`
	// ScrollbackHydrationBytes is how many bytes of the scrollback tail are
	// replayed into an adopted session's virtual screen at adopt time. Values < 0
	// fall back to the default (ScrollbackHydrationBytesDefault); "0" disables
	// hydration.
	ScrollbackHydrationBytes int `toml:"scrollback_hydration_bytes"`
	// InputDelay is the pause between writing text and the submit carriage return
	// in WriteInputAndSubmit, so a TUI doesn't treat text+CR as a paste. Empty,
	// unparseable, or non-positive uses the default (InputDelayDefault) — a zero
	// pause would defeat the paste guard. Applies to sessions launched after the
	// change.
	InputDelay string `toml:"input_delay"`
	// DefaultCols / DefaultRows are the terminal geometry used by daemon launch
	// paths (watchdog restart, orchestrator, scenarios, triggers, adoption) when
	// no client geometry is available. Values < 1 fall back to the defaults
	// (DefaultColsDefault / DefaultRowsDefault). Applies to sessions launched
	// after the change; an attaching client resizes to its real geometry.
	DefaultCols int `toml:"default_cols"`
	DefaultRows int `toml:"default_rows"`
	// MaxLogBytes caps the per-session scrollback log file. Values < 0 fall back
	// to the default (MaxLogBytesDefault); "0" means unlimited. Applies to sessions
	// launched (or adopted) after the change.
	MaxLogBytes int64 `toml:"max_log_bytes"`
}

LifecycleConfig is the [lifecycle] block gathering the session-lifecycle and PTY policy that was previously spread as fixed constants and bare literals across the daemon, headless, and pty packages (issue #1243): the convert-to-interactive signal-escalation waits, the headless interrupt round-trip, mass-exit detection, the process-teardown grace, adopted-PTY babysit timing, scrollback hydration, terminal-input pacing, the default launch geometry, and the per-session log cap.

The signal-escalation ORDER (interrupt → SIGTERM → SIGKILL) stays a code invariant; only the wait durations between steps are tunable here. Every field is optional: an empty/unparseable/out-of-range value falls back to the matching default constant, preserving the historical behaviour. Geometry and log/ hydration limits apply only to sessions launched (or adopted) after the change; running sessions keep the geometry and caps they started with.

func (LifecycleConfig) AdoptedPollIntervalDuration added in v0.69.1

func (l LifecycleConfig) AdoptedPollIntervalDuration() time.Duration

AdoptedPollIntervalDuration returns the adopted-PTY poll cadence, or the default when unset, unparseable, or non-positive (a zero cadence would busy-loop).

func (LifecycleConfig) AdoptedTimeoutDuration added in v0.69.1

func (l LifecycleConfig) AdoptedTimeoutDuration() time.Duration

AdoptedTimeoutDuration returns the adopted-PTY safety deadline, or the default when unset, unparseable, or non-positive.

func (LifecycleConfig) ConvertForceKillTimeoutDuration added in v0.69.1

func (l LifecycleConfig) ConvertForceKillTimeoutDuration() time.Duration

ConvertForceKillTimeoutDuration returns the post-SIGKILL wait, or the default when unset, unparseable, or non-positive.

func (LifecycleConfig) ConvertKillTimeoutDuration added in v0.69.1

func (l LifecycleConfig) ConvertKillTimeoutDuration() time.Duration

ConvertKillTimeoutDuration returns the SIGTERM-step wait, or the default when unset, unparseable, or non-positive.

func (LifecycleConfig) ConvertSettleTimeoutDuration added in v0.69.1

func (l LifecycleConfig) ConvertSettleTimeoutDuration() time.Duration

ConvertSettleTimeoutDuration returns the interrupt→settle wait, or the default when unset, unparseable, or non-positive.

func (LifecycleConfig) DefaultColsOrDefault added in v0.69.1

func (l LifecycleConfig) DefaultColsOrDefault() uint16

DefaultColsOrDefault returns the default launch column count, or the default when < 1.

func (LifecycleConfig) DefaultRowsOrDefault added in v0.69.1

func (l LifecycleConfig) DefaultRowsOrDefault() uint16

DefaultRowsOrDefault returns the default launch row count, or the default when < 1.

func (LifecycleConfig) InputDelayDuration added in v0.69.1

func (l LifecycleConfig) InputDelayDuration() time.Duration

InputDelayDuration returns the type-then-submit pause, or the default when unset, unparseable, or non-positive (a zero pause would defeat the paste guard).

func (LifecycleConfig) MassExitThresholdOrDefault added in v0.69.1

func (l LifecycleConfig) MassExitThresholdOrDefault() int

MassExitThresholdOrDefault returns the mass-exit exit-count threshold, or the default when < 1 (a zero threshold has no meaningful trigger).

func (LifecycleConfig) MassExitWindowDuration added in v0.69.1

func (l LifecycleConfig) MassExitWindowDuration() time.Duration

MassExitWindowDuration returns the mass-exit detection window, or the default when unset, unparseable, or non-positive.

func (LifecycleConfig) MaxLogBytesOrDefault added in v0.69.1

func (l LifecycleConfig) MaxLogBytesOrDefault() int64

MaxLogBytesOrDefault returns the per-session log cap. A value < 0 means "use the default"; "0" is honoured (unlimited, as the scrollback writer treats a non-positive cap as no limit).

func (LifecycleConfig) ProcessKillGraceDuration added in v0.69.1

func (l LifecycleConfig) ProcessKillGraceDuration() time.Duration

ProcessKillGraceDuration returns the SIGTERM→SIGKILL grace, or the default when unset, unparseable, or non-positive.

func (LifecycleConfig) ScrollbackHydrationBytesOrDefault added in v0.69.1

func (l LifecycleConfig) ScrollbackHydrationBytesOrDefault() int

ScrollbackHydrationBytesOrDefault returns the adopt-time hydration size. A value < 0 means "use the default"; "0" is honoured (disable hydration).

type LimitsConfig added in v0.69.1

type LimitsConfig struct {
	// LogLines is the default number of trailing output lines shown when a
	// `lines`/`-n` count is not given: `gr logs`, `gr mcp logs`, the scrollback
	// replayed to a client on attach, and the MCP log reader all share it. Values
	// < 1 fall back to the default (LimitsLogLinesDefault).
	LogLines int `toml:"log_lines"`
	// WaitScanLines bounds how much existing scrollback `gr wait --contains`
	// scans for an already-present match before it starts following live output.
	// Values < 1 fall back to the default (LimitsWaitScanLinesDefault).
	WaitScanLines int `toml:"wait_scan_lines"`
	// WaitBufferBytes bounds the retained partial line in the live `gr wait`
	// matcher so a long stream without a newline can't grow the buffer without
	// limit. Values < 1 fall back to the default (LimitsWaitBufferBytesDefault).
	WaitBufferBytes int `toml:"wait_buffer_bytes"`
	// MCPLogReadBytes bounds how many trailing bytes of an MCP server log file
	// are read before splitting into lines, keeping a huge log from being loaded
	// whole. Values < 1 fall back to the default (LimitsMCPLogReadBytesDefault).
	MCPLogReadBytes int `toml:"mcp_log_read_bytes"`
	// ApprovalDisplayBytes caps the tool input shown in the approval overlay and
	// broadcast to attached clients (the full input is still what backends
	// evaluate). Values < 1 fall back to the default
	// (LimitsApprovalDisplayBytesDefault).
	ApprovalDisplayBytes int `toml:"approval_display_bytes"`
	// LastMessageRunes bounds the agent's final Stop message the status hook
	// forwards to the daemon, so a large final output never becomes an unbounded
	// control frame. Counted in runes (never splits a multi-byte character).
	// Values < 1 fall back to the default (LimitsLastMessageRunesDefault).
	LastMessageRunes int `toml:"last_message_runes"`
	// InboxPreviewBytes bounds the unread-inbox preview injected into a session's
	// SessionStart hook context. Values < 1 fall back to the default
	// (LimitsInboxPreviewBytesDefault).
	InboxPreviewBytes int `toml:"inbox_preview_bytes"`
}

LimitsConfig is the [limits] block gathering the user-visible output, log, wait, and display truncation caps that were previously duplicated as unrelated Go constants and literals across the daemon, CLI, and MCP manager (issue #1252). Unifying them means changing one place updates every surface. Every field is optional: a value < 1 falls back to the matching default constant, preserving the historical behaviour. Units are stated in each field name (lines, bytes, runes) so a single number is unambiguous.

func (LimitsConfig) ApprovalDisplayBytesOrDefault added in v0.69.1

func (l LimitsConfig) ApprovalDisplayBytesOrDefault() int

ApprovalDisplayBytesOrDefault returns the approval-overlay display cap, clamped to a sensible minimum. A value < 1 means "use the default".

func (LimitsConfig) InboxPreviewBytesOrDefault added in v0.69.1

func (l LimitsConfig) InboxPreviewBytesOrDefault() int

InboxPreviewBytesOrDefault returns the inbox-preview byte cap, clamped to a sensible minimum. A value < 1 means "use the default".

func (LimitsConfig) LastMessageRunesOrDefault added in v0.69.1

func (l LimitsConfig) LastMessageRunesOrDefault() int

LastMessageRunesOrDefault returns the hook last-message rune cap, clamped to a sensible minimum. A value < 1 means "use the default".

func (LimitsConfig) LogLinesOrDefault added in v0.69.1

func (l LimitsConfig) LogLinesOrDefault() int

LogLinesOrDefault returns the default log-tail line count, clamped to a sensible minimum. A value < 1 means "use the default".

func (LimitsConfig) MCPLogReadBytesOrDefault added in v0.69.1

func (l LimitsConfig) MCPLogReadBytesOrDefault() int

MCPLogReadBytesOrDefault returns the MCP log read cap, clamped to a sensible minimum. A value < 1 means "use the default".

func (LimitsConfig) WaitBufferBytesOrDefault added in v0.69.1

func (l LimitsConfig) WaitBufferBytesOrDefault() int

WaitBufferBytesOrDefault returns the `gr wait` matcher partial-line cap, clamped to a sensible minimum. A value < 1 means "use the default".

func (LimitsConfig) WaitScanLinesOrDefault added in v0.69.1

func (l LimitsConfig) WaitScanLinesOrDefault() int

WaitScanLinesOrDefault returns the `gr wait` scrollback-scan line count, clamped to a sensible minimum. A value < 1 means "use the default".

type MCPServerConfig added in v0.22.0

type MCPServerConfig struct {
	Name          string            `json:"-"              toml:"name"`
	Command       string            `json:"command"        toml:"command"`
	Args          []string          `json:"args,omitempty" toml:"args,omitempty"`
	Env           map[string]string `json:"env,omitempty"  toml:"env,omitempty"`
	Disabled      bool              `json:"-"              toml:"disabled,omitempty"`
	Sandbox       *bool             `json:"-"              toml:"sandbox,omitempty"`
	SandboxConfig *SandboxConfig    `json:"-"              toml:"sandbox_config,omitempty"`
}

func MergeMCPServers added in v0.22.0

func MergeMCPServers(global []MCPServerConfig, overrides map[string]MCPServerConfig) []MCPServerConfig

type Messages added in v0.3.0

type Messages struct {
	// MaxAge is the message-retention window: messages older than this are swept
	// by the cleanup loop. An empty value or an explicit "0" is the documented
	// "retain forever" sentinel (no age-based cleanup). A non-empty value is
	// validated at load: an unparseable or negative duration is rejected so a typo
	// cannot silently turn a bounded-retention deployment into keep-forever
	// (issue #1321). Reloadable.
	MaxAge       string `toml:"max_age"`
	MaxPerStream int    `toml:"max_per_stream"`
	// ConversationPageSize is the page size applied when a msg_conversation
	// request supplies a non-positive limit. Values < 1 fall back to the default
	// (MessagesConversationPageSizeDefault). Reloadable.
	ConversationPageSize int `toml:"conversation_page_size"`
	// ConversationMaxLimit is the hard cap on how many messages a single
	// msg_conversation request may sort, bounding a local perf/DoS footgun. Values
	// < 1 fall back to the default (MessagesConversationMaxLimitDefault); a value
	// above MessagesConversationMaxLimitCeiling is rejected at load. Reloadable.
	ConversationMaxLimit int `toml:"conversation_max_limit"`
	// JailListLimit caps how many quarantined comments a jail listing returns
	// (newest first), so the query can't force an unbounded allocation. Values < 1
	// fall back to the default (MessagesJailListLimitDefault); a value above
	// MessagesJailListLimitCeiling is rejected at load. Reloadable.
	JailListLimit int `toml:"jail_list_limit"`
	// SubscriberBuffer is the per-subscriber pub/sub channel capacity. A slow
	// reader that fills its buffer drops further messages until it drains (the
	// stored log stays authoritative), so this is a load-tuning knob for
	// installations with bursty fan-out. Values < 1 fall back to the default
	// (MessagesSubscriberBufferDefault); a value above
	// MessagesSubscriberBufferCeiling is rejected at load. Restart-only.
	SubscriberBuffer int `toml:"subscriber_buffer"`
	// BusyTimeout is the SQLite busy_timeout for the messages database — how long
	// a contended operation waits for the lock before erroring. It is graith's
	// database operation deadline. Empty/unset uses the default
	// (MessagesBusyTimeoutDefault, 5s). An explicit value must parse and fall in
	// [SQLiteBusyTimeoutResolution, MessagesBusyTimeoutCeiling] (1ms–5m):
	// SQLite's busy_timeout has millisecond resolution, so a positive sub-1ms
	// value would collapse to busy_timeout(0) and disable lock waiting, and it —
	// along with any unparseable, non-positive, or above-ceiling value — is
	// rejected at load. Restart-only.
	BusyTimeout string `toml:"busy_timeout"`
}

Messages is the [messages] block. It governs the message-log subsystem: the cleanup retention (MaxAge/MaxPerStream) plus the operational limits made configurable by issue #1249 — conversation paging bounds, the jail listing cap, the pub/sub subscriber buffer, and the SQLite busy/operation timeout.

SubscriberBuffer and BusyTimeout are fixed at store-open time, so a change to either takes effect only on daemon restart. The conversation paging bounds and the jail cap are read per-request, so they apply on reload.

func (Messages) BusyTimeoutDuration added in v0.69.1

func (m Messages) BusyTimeoutDuration() time.Duration

BusyTimeoutDuration returns the messages-database SQLite busy_timeout, or the default when unset, unparseable, or non-positive (a zero/negative timeout would make a contended write fail immediately instead of waiting).

func (Messages) ClampConversationLimit added in v0.69.1

func (m Messages) ClampConversationLimit(limit int) int

ClampConversationLimit normalizes a client-supplied conversation limit: a non-positive limit becomes the configured page size, and any limit above the configured maximum is capped at it.

func (Messages) ConversationMaxLimitOrDefault added in v0.69.1

func (m Messages) ConversationMaxLimitOrDefault() int

ConversationMaxLimitOrDefault returns the hard cap on a single conversation sort. A non-positive value means "use the default".

func (Messages) ConversationPageSizeOrDefault added in v0.69.1

func (m Messages) ConversationPageSizeOrDefault() int

ConversationPageSizeOrDefault returns the default conversation page size (used when a request supplies a non-positive limit). A non-positive configured value means "use the default"; the result is additionally clamped to the effective max limit so a misconfigured page size can never exceed the hard cap.

func (Messages) JailListLimitOrDefault added in v0.69.1

func (m Messages) JailListLimitOrDefault() int

JailListLimitOrDefault returns the jail listing row cap. A non-positive value means "use the default".

func (Messages) MaxAgeDuration added in v0.3.0

func (m Messages) MaxAgeDuration() time.Duration

MaxAgeDuration returns the message-retention window. An empty value or an explicit zero is the documented "retain forever" sentinel (returns 0, which runMessageCleanupFromConfig treats as no age-based cleanup). Load/reload validation rejects a non-empty unparseable or negative value; this accessor keeps a defensive fallback to 0 (retain forever) for directly-constructed configs so a garbage value can never turn into a negative window that would otherwise select a future cutoff and delete everything.

func (Messages) SubscriberBufferOrDefault added in v0.69.1

func (m Messages) SubscriberBufferOrDefault() int

SubscriberBufferOrDefault returns the per-subscriber channel capacity. A non-positive value means "use the default".

type MigrationConfig added in v0.69.1

type MigrationConfig struct {
	// HealthWindow is how long Migrate waits to confirm the target agent survived
	// startup before declaring the migration successful. Empty/unparseable/
	// non-positive uses MigrationHealthWindowDefault.
	HealthWindow string `toml:"health_window"`
}

MigrationConfig is the [migration] block tuning the cross-agent conversation migration (issue #1250). HealthWindow is optional: empty, unparseable, or non-positive falls back to MigrationHealthWindowDefault.

func (MigrationConfig) HealthWindowDuration added in v0.69.1

func (m MigrationConfig) HealthWindowDuration() time.Duration

HealthWindowDuration returns the migration startup-health window, or the default when unset, unparseable, or non-positive.

type NotificationTiming added in v0.69.1

type NotificationTiming struct {
	// CoalesceWindow is how long an identical (title+message+priority) push
	// notification is dropped as a duplicate, coalescing rapid-fire events. Empty
	// uses the default (NotifyCoalesceWindowDefault); "0" disables coalescing.
	CoalesceWindow string `toml:"coalesce_window"`
	// DispatchTimeout bounds a single backend dispatch (osascript / notifier app /
	// command) so a hung helper can't block the caller. Empty or non-positive uses
	// the default (NotifyDispatchTimeoutDefault).
	DispatchTimeout string `toml:"dispatch_timeout"`
	// InboxIdleTimeout is how long an attached session's PTY must be free of user
	// input before an inbox notification or `gr type` input is injected, so it
	// doesn't land mid-type. Empty or non-positive uses the default
	// (NotifyInboxIdleTimeoutDefault).
	InboxIdleTimeout string `toml:"inbox_idle_timeout"`
	// InboxMaxWait caps the total wait for user idle before an inbox notification
	// or `gr type` input is injected regardless. Empty or non-positive uses the
	// default (NotifyInboxMaxWaitDefault).
	InboxMaxWait string `toml:"inbox_max_wait"`
	// InboxCooldown is the minimum interval between unread-inbox notifications to
	// one session, throttling repeat nudges. Empty uses the default
	// (NotifyInboxCooldownDefault); "0" disables the cooldown.
	InboxCooldown string `toml:"inbox_cooldown"`
	// InboxDetachedDelay is the settle delay before notifying a session with no
	// attached client (no user-idle signal to wait on). Empty uses the default
	// (NotifyInboxDetachedDelayDefault); "0" notifies immediately.
	InboxDetachedDelay string `toml:"inbox_detached_delay"`
}

NotificationTiming gathers the notification timing policy that was previously spread as fixed constants across the daemon's push (pushnotify.go) and inbox-notification (notify.go) paths (issue #1245). Every field is optional: an empty or unparseable value falls back to the matching default constant, preserving the historical behaviour.

func (NotificationTiming) CoalesceWindowDuration added in v0.69.1

func (t NotificationTiming) CoalesceWindowDuration() time.Duration

CoalesceWindowDuration returns the push-notification coalescing window, or the default when unset or unparseable. A "0" disables coalescing.

func (NotificationTiming) DispatchTimeoutDuration added in v0.69.1

func (t NotificationTiming) DispatchTimeoutDuration() time.Duration

DispatchTimeoutDuration returns the per-backend dispatch timeout, or the default when unset, unparseable, or non-positive (a zero timeout would fail every dispatch instantly).

func (NotificationTiming) InboxCooldownDuration added in v0.69.1

func (t NotificationTiming) InboxCooldownDuration() time.Duration

InboxCooldownDuration returns the minimum interval between unread-inbox notifications to one session, or the default when unset or unparseable. A "0" disables the cooldown.

func (NotificationTiming) InboxDetachedDelayDuration added in v0.69.1

func (t NotificationTiming) InboxDetachedDelayDuration() time.Duration

InboxDetachedDelayDuration returns the settle delay before notifying a detached session, or the default when unset or unparseable. A "0" notifies immediately.

func (NotificationTiming) InboxIdleTimeoutDuration added in v0.69.1

func (t NotificationTiming) InboxIdleTimeoutDuration() time.Duration

InboxIdleTimeoutDuration returns the user-idle wait before an inbox notification or `gr type` input is injected, or the default when unset, unparseable, or non-positive.

func (NotificationTiming) InboxMaxWaitDuration added in v0.69.1

func (t NotificationTiming) InboxMaxWaitDuration() time.Duration

InboxMaxWaitDuration returns the cap on the shared inbox-notification and `gr type` user-idle wait, or the default when unset, unparseable, or non-positive.

type Notifications added in v0.2.0

type Notifications struct {
	Enabled    bool   `toml:"enabled"`
	OnApproval bool   `toml:"on_approval"`
	OnStopped  bool   `toml:"on_stopped"`
	Command    string `toml:"command"`
	// Backend selects how proactive `gr notify` push notifications are delivered:
	// "macos" (osascript desktop notification; the default when unset) or
	// "command" (run [notifications] command with GRAITH_NOTIFY_* env vars). Other
	// backends (ntfy/pushover/slack) are planned follow-ups and rejected for now.
	Backend string `toml:"backend"`
	// MaxPerHour rate-limits low/normal push notifications over a rolling hour so a
	// misbehaving trigger can't storm the user. <=0 uses DefaultNotifyMaxPerHour.
	// High-priority notifications bypass this limit.
	MaxPerHour int `toml:"max_per_hour"`
	// QuietHoursStart / QuietHoursEnd define a daily window ("HH:MM", 24-hour) in
	// which low/normal push notifications are suppressed. The window may wrap past
	// midnight (start > end, e.g. 22:00-07:00). Both must be set to take effect.
	// High-priority notifications bypass quiet hours.
	QuietHoursStart string `toml:"quiet_hours_start"`
	QuietHoursEnd   string `toml:"quiet_hours_end"`
	// Timing carries the low-level coalescing, dispatch, and PTY-injection
	// timing knobs under [notifications.timing]. Every field is optional and
	// resolves to its documented default through the NotificationTiming accessors,
	// so leaving the table out preserves the historical behaviour (issue #1245).
	Timing NotificationTiming `toml:"timing"`
}

func (Notifications) InQuietHours added in v0.67.2

func (n Notifications) InQuietHours(t time.Time) bool

InQuietHours reports whether the local time t falls within the configured quiet-hours window. It supports a window that wraps past midnight (start > end). An unset or unparseable window returns false (fail-open: a typo mutes nothing rather than everything — Validate rejects a malformed window at load).

func (Notifications) MaxPerHourValue added in v0.67.2

func (n Notifications) MaxPerHourValue() int

MaxPerHourValue returns the effective rolling-hour push-notification cap, defaulting to DefaultNotifyMaxPerHour when unset (<=0).

func (Notifications) NotifyBackendName added in v0.67.2

func (n Notifications) NotifyBackendName() string

NotifyBackendName returns the effective push-notification backend, defaulting to "macos" when unset.

func (Notifications) QuietHoursConfigured added in v0.67.2

func (n Notifications) QuietHoursConfigured() bool

QuietHoursConfigured reports whether a quiet-hours window is fully set.

func (Notifications) Validate added in v0.67.2

func (n Notifications) Validate() error

Validate checks the [notifications] block for static errors: an unknown push backend, a malformed quiet-hours window, or a "command" backend with no command set. It fails closed so a typo surfaces at config-load rather than as a silent no-op notification.

type OrchestratorConfig added in v0.42.0

type OrchestratorConfig struct {
	Enabled     bool                      `toml:"enabled"`
	Agent       string                    `toml:"agent"`
	Model       string                    `toml:"model"`
	IdleTimeout string                    `toml:"idle_timeout"`
	Prompt      string                    `toml:"prompt"`
	PromptFile  string                    `toml:"prompt_file"`
	Sandbox     OrchestratorSandboxConfig `toml:"sandbox"`
	Restart     OrchestratorRestartConfig `toml:"restart"`
}

func (OrchestratorConfig) AgentName added in v0.42.0

func (o OrchestratorConfig) AgentName(defaultAgent string) string

AgentName resolves the agent type the orchestrator session runs as. An explicit [orchestrator] agent wins; otherwise it inherits the top-level default_agent (passed in by the caller, which has access to the full config), falling back to "claude" only when neither is set.

func (OrchestratorConfig) IdleTimeoutDuration added in v0.42.0

func (o OrchestratorConfig) IdleTimeoutDuration() time.Duration

type OrchestratorRestartConfig added in v0.69.1

type OrchestratorRestartConfig struct {
	// InitialBackoff is the first restart delay in geometric mode (Schedule empty).
	// Empty uses OrchestratorInitialBackoffDefault.
	InitialBackoff string `toml:"initial_backoff"`
	// MaxBackoff caps the restart delay in geometric mode. Empty uses
	// OrchestratorMaxBackoffDefault.
	MaxBackoff string `toml:"max_backoff"`
	// Multiplier grows the delay each attempt in geometric mode. Values <= 1 fall
	// back to OrchestratorMultiplierDefault.
	Multiplier float64 `toml:"multiplier"`
	// Schedule is an explicit list of per-attempt delays. When set it overrides
	// the geometric knobs; the last entry repeats for attempts beyond its length.
	Schedule []string `toml:"schedule"`
	// StableReset is how long a run must last before its exit resets the backoff
	// level to 0. Empty uses OrchestratorStableResetDefault.
	StableReset string `toml:"stable_reset"`
	// FreshStartThreshold is the number of consecutive restarts after which the
	// orchestrator is relaunched fresh (new agent session id). Values < 1 fall
	// back to OrchestratorFreshStartThresholdDefault.
	FreshStartThreshold int `toml:"fresh_start_threshold"`
}

OrchestratorRestartConfig tunes how the daemon auto-restarts the orchestrator after it exits unexpectedly (crash/watchdog, not a user/idle/shutdown stop).

Two backoff modes are supported. When Schedule is non-empty it wins: it is the explicit list of per-attempt delays, and the final entry repeats for every attempt beyond its length. When Schedule is empty a geometric backoff is computed as InitialBackoff × Multiplier^level, capped at MaxBackoff.

func (OrchestratorRestartConfig) DelayForLevel added in v0.69.1

func (r OrchestratorRestartConfig) DelayForLevel(level int) time.Duration

DelayForLevel returns the restart delay for a given (0-based) backoff level. An explicit Schedule wins, with its final entry repeating past its length; otherwise the delay is computed geometrically. With no restart config at all this reproduces graith's historical schedule (2s,4s,8s,16s,32s,60s,300s).

func (OrchestratorRestartConfig) FreshStartThresholdOrDefault added in v0.69.1

func (r OrchestratorRestartConfig) FreshStartThresholdOrDefault() int

FreshStartThresholdOrDefault returns the consecutive-restart count that triggers a fresh start. Non-positive values fall back to the default.

func (OrchestratorRestartConfig) StableResetDuration added in v0.69.1

func (r OrchestratorRestartConfig) StableResetDuration() time.Duration

StableResetDuration is how long a run must last before its exit resets the backoff level. Empty, unparseable, or non-positive uses OrchestratorStableResetDefault.

type OrchestratorSandboxConfig added in v0.46.0

type OrchestratorSandboxConfig struct {
	ReadDirs   []string `toml:"read_dirs"`
	WriteDirs  []string `toml:"write_dirs"`
	ReadFiles  []string `toml:"read_files"`
	WriteFiles []string `toml:"write_files"`
}

type Overlay added in v0.56.0

type Overlay struct {
	ShortcutKeys string `toml:"shortcut_keys"`
}

type OverlayKeybindings added in v0.69.1

type OverlayKeybindings struct {
	// Shared navigation, applied across the overlays.
	Up       string `toml:"up"`
	Down     string `toml:"down"`
	PageUp   string `toml:"page_up"`
	PageDown string `toml:"page_down"`
	Top      string `toml:"top"`
	Bottom   string `toml:"bottom"`
	Confirm  string `toml:"confirm"`
	Cancel   string `toml:"cancel"`
	// Dashboard actions.
	DashboardAttach string `toml:"dashboard_attach"`
	DashboardStop   string `toml:"dashboard_stop"`
	DashboardDelete string `toml:"dashboard_delete"`
	DashboardResume string `toml:"dashboard_resume"`
	// Approval prompt actions.
	ApprovalAllow    string `toml:"approval_allow"`
	ApprovalDeny     string `toml:"approval_deny"`
	ApprovalAllowAll string `toml:"approval_allow_all"`
	// Message viewer actions.
	MessagePin         string `toml:"message_pin"`
	MessageExpandAll   string `toml:"message_expand_all"`
	MessageCollapseAll string `toml:"message_collapse_all"`
	MessageNextConv    string `toml:"message_next_conversation"`
	MessagePrevConv    string `toml:"message_prev_conversation"`
}

OverlayKeybindings configures the keys used inside the terminal TUI overlays. Every value is a space-separated list of bubbletea key names (single letters, "up", "down", "enter", "esc", "pgup", "ctrl+d", ...); pressing any listed key triggers the action. Empty fields fall back to the built-in defaults, so a partial [keybindings.overlay] table only overrides the keys it names.

type PRWatchAdvancedConfig added in v0.69.1

type PRWatchAdvancedConfig struct {
	// BaseTick is the base poll-loop cadence (per-session gating paces the actual
	// gh calls below it). Default 15s. Applied when the watch loop starts.
	BaseTick string `toml:"base_tick"`
	// BatchSize caps how many sessions are polled per tick, bounding gh load on a
	// large fleet. Default 3.
	BatchSize int `toml:"batch_size"`
	// NoPRNegativeCache is how long a branch with no PR is left before re-resolving
	// on the ordinary timer. Default 5m.
	NoPRNegativeCache string `toml:"no_pr_negative_cache"`
	// CommentBodyMaxBytes truncates each delivered PR-comment body to this many
	// bytes, bounding notification size. Default 1024.
	CommentBodyMaxBytes int `toml:"comment_body_max_bytes"`
	// NotificationRateLimit / NotificationRateWindow are the per-session rolling
	// anti-thrash backstop: at most this many notifications per window to one
	// session. Defaults 5 per 30m.
	NotificationRateLimit  int    `toml:"notification_rate_limit"`
	NotificationRateWindow string `toml:"notification_rate_window"`
	// UntrustedAuthorPromptRate / UntrustedAuthorPromptWindow bound the untrusted
	// comment-author trust prompt to the orchestrator (a security surface — a busy
	// public PR churning drive-by commenters must not flood it). Defaults 5 per 30m.
	UntrustedAuthorPromptRate   int    `toml:"untrusted_author_prompt_rate"`
	UntrustedAuthorPromptWindow string `toml:"untrusted_author_prompt_window"`
	// MaxPromptedAuthors bounds the persisted set of already-surfaced untrusted
	// authors so it can't grow without limit. Default 5000.
	MaxPromptedAuthors int `toml:"max_prompted_authors"`
	// KickCooldown is the minimum interval between git-ref-triggered immediate polls
	// of one session (belt-and-braces over the ref-watch debounce). Default 3s.
	KickCooldown string `toml:"kick_cooldown"`
	// KickChannelSize is the buffered kick-channel capacity; a full channel drops
	// the (best-effort) kick. Default 64. Applied when the watch state is built.
	KickChannelSize int `toml:"kick_channel_size"`
	// KickedNoPRBackoff is the short re-poll delay after a kicked poll finds no PR
	// yet (a push is usually moments before `gh pr create`), instead of parking on
	// the full negative cache. Default 20s.
	KickedNoPRBackoff string `toml:"kicked_no_pr_backoff"`
	// RefReconcileInterval is how often the git-ref watcher set is reconciled
	// against live sessions. Default 2s. Applied when the ref-watch loop starts.
	RefReconcileInterval string `toml:"ref_reconcile_interval"`
	// RefDebounce coalesces the burst of ref/reflog writes one push/commit/checkout
	// produces into a single kick. Default 750ms.
	RefDebounce string `toml:"ref_debounce"`
	// GHTimeout is the per-command timeout for the daemon's `gh` invocations, so a
	// hung gh can never stall the loop. Default 5s.
	GHTimeout string `toml:"gh_timeout"`
}

PRWatchAdvancedConfig carries the advanced tuning for the PR/CI watch loop and its git-ref accelerator. These were formerly hard-coded policy literals in the daemon; they are surfaced here so an operator can tune load, latency, retention, and the untrusted-author prompt-injection surface without a rebuild. Every field is optional: an unset (zero) value resolves to the documented default through the PRWatchConfig accessors, so leaving [pr_watch.advanced] out is a no-op.

type PRWatchConfig added in v0.59.0

type PRWatchConfig struct {
	Enabled               bool   `toml:"enabled"`
	NotifyCIFailures      bool   `toml:"notify_ci_failures"`
	NotifyMergeConflicts  bool   `toml:"notify_merge_conflicts"`
	NotifyReviewComments  bool   `toml:"notify_review_comments"`
	NotifyPRComments      bool   `toml:"notify_pr_comments"`
	NotifyReviewDecisions bool   `toml:"notify_review_decisions"`
	NotifyPRLifecycle     bool   `toml:"notify_pr_lifecycle"`
	NotifyCIRecovery      bool   `toml:"notify_ci_recovery"`
	PollPending           string `toml:"poll_pending"`
	PollTerminal          string `toml:"poll_terminal"`
	PollMerged            string `toml:"poll_merged"`
	MaxNotificationsPerPR int    `toml:"max_notifications_per_pr"`
	Debounce              string `toml:"debounce"`
	// CommentAuthorAllowlist trusts individual comment authors by login,
	// case-insensitively and matched against the full "<name>[bot]" string. It is
	// the ONLY way to trust a bot or GitHub App (their author_association is
	// unreliable — a bot can carry NONE or CONTRIBUTOR), and also covers named
	// humans. Defaults empty; discovery is via the orchestrator trust prompt.
	CommentAuthorAllowlist []string `toml:"comment_author_allowlist"`
	// TrustedAuthorAssociations is the set of GitHub author_association values
	// treated as trusted. Defaults to OWNER/MEMBER/COLLABORATOR when unset (the
	// "has write access to, or is a member of the org that owns, the repo" tier);
	// CONTRIBUTOR is deliberately excluded. Values are normalised to upper-case
	// via TrustedAssociationSet.
	TrustedAuthorAssociations []string `toml:"trusted_author_associations"`
	// NotifyUntrustedAuthors, when true, sends a one-time metadata-only message to
	// the orchestrator the first time a comment from a not-yet-trusted author is
	// seen, so the human can decide whether to allowlist them. It NEVER carries
	// the untrusted comment body. False disables the prompt entirely (silent drop,
	// still logged).
	NotifyUntrustedAuthors bool `toml:"notify_untrusted_authors"`
	// Advanced holds the low-level watcher-tuning knobs (loop cadence, batch size,
	// caches, rate limits, ref-watch timing, gh timeout). Every field is optional
	// and falls back to a sensible default via the accessors below, so a config
	// that omits [pr_watch.advanced] entirely behaves exactly as before. Expose
	// these only for operators who need to trade off load, latency, retention, and
	// prompt-injection surface — the defaults suit ordinary use.
	Advanced PRWatchAdvancedConfig `toml:"advanced"`
}

PRWatchConfig controls the PR & CI awareness loop, which resolves each session's GitHub PR via the gh CLI, polls its CI checks and review comments, and notifies the owning session's inbox on meaningful transitions.

Every notify_* sub-option defaults on: enabling pr_watch is meant to be a single switch (enabled = true) that turns on all notifications, and users selectively disable the classes they don't want. The classes are still gated separately because they carry different authority — a CI failure is a machine verdict (safe to act on), while a review comment or decision is human intent that may not be actionable — so each can be turned off independently.

Comments come in two distinct kinds, each with its own gate:

  • NotifyReviewComments covers inline code-review comments (the pulls/{n}/comments surface) — feedback anchored to a file and line.
  • NotifyPRComments covers regular conversation comments on the PR thread (the issues/{n}/comments surface) — issue-style comments not tied to a line of code.

They are separate signals: a reviewer leaving inline nits and someone dropping a "ship it" on the conversation thread differ, and a user may want one without the other.

For backward compatibility, notify_pr_comments used to be folded into notify_review_comments; see applyPRWatchCommentCompat, which keeps an older config that only set notify_review_comments delivering conversation comments.

func (PRWatchConfig) BaseTickDuration added in v0.69.1

func (p PRWatchConfig) BaseTickDuration() time.Duration

BaseTickDuration is the base poll-loop cadence. Default 15s; an unset, unparseable, or non-positive value uses the default (the poll loop feeds this straight to time.NewTicker, which panics on a non-positive interval).

func (PRWatchConfig) BatchSize added in v0.69.1

func (p PRWatchConfig) BatchSize() int

BatchSize caps sessions polled per tick. Default 3.

func (PRWatchConfig) CommentBodyMaxBytes added in v0.69.1

func (p PRWatchConfig) CommentBodyMaxBytes() int

CommentBodyMaxBytes is the per-comment body truncation cap. Default 1024.

func (PRWatchConfig) DebounceDuration added in v0.59.0

func (p PRWatchConfig) DebounceDuration() time.Duration

DebounceDuration is the minimum cooldown between notifications to one session.

func (PRWatchConfig) GHTimeoutDuration added in v0.69.1

func (p PRWatchConfig) GHTimeoutDuration() time.Duration

GHTimeoutDuration is the per-`gh`-command timeout. Default 5s.

func (PRWatchConfig) KickChannelSize added in v0.69.1

func (p PRWatchConfig) KickChannelSize() int

KickChannelSize is the buffered kick-channel capacity. Default 64, maximum 4096. Directly constructed configs above the maximum are defensively capped; loaded configs reject them during validation.

func (PRWatchConfig) KickCooldownDuration added in v0.69.1

func (p PRWatchConfig) KickCooldownDuration() time.Duration

KickCooldownDuration is the min interval between git-ref-triggered polls of one session. Default 3s.

func (PRWatchConfig) KickedNoPRBackoffDuration added in v0.69.1

func (p PRWatchConfig) KickedNoPRBackoffDuration() time.Duration

KickedNoPRBackoffDuration is the short re-poll delay after a kicked no-PR miss. Default 20s.

func (PRWatchConfig) MaxNotifications added in v0.59.0

func (p PRWatchConfig) MaxNotifications() int

MaxNotifications returns the per-head-SHA notification cap, defaulting to 10.

func (PRWatchConfig) MaxPromptedAuthors added in v0.69.1

func (p PRWatchConfig) MaxPromptedAuthors() int

MaxPromptedAuthors bounds the persisted surfaced-authors set. Default 5000.

func (PRWatchConfig) NoPRNegativeCacheDuration added in v0.69.1

func (p PRWatchConfig) NoPRNegativeCacheDuration() time.Duration

NoPRNegativeCacheDuration is the no-PR re-resolve interval. Default 5m.

func (PRWatchConfig) NotificationRateLimit added in v0.69.1

func (p PRWatchConfig) NotificationRateLimit() int

NotificationRateLimit is the per-session rolling notification cap. Default 5.

func (PRWatchConfig) NotificationRateWindowDuration added in v0.69.1

func (p PRWatchConfig) NotificationRateWindowDuration() time.Duration

NotificationRateWindowDuration is the per-session rate-limit window. Default 30m; an unset, unparseable, or non-positive value uses the default. This window bounds a rolling anti-thrash cap (NotificationRateLimit), so a zero/negative value must not slip through: it would prune every prior timestamp and disable the cap (issue #1304).

func (PRWatchConfig) PollMergedDuration added in v0.59.0

func (p PRWatchConfig) PollMergedDuration() time.Duration

PollMergedDuration is the sweep interval for merged/closed PRs.

func (PRWatchConfig) PollPendingDuration added in v0.59.0

func (p PRWatchConfig) PollPendingDuration() time.Duration

PollPendingDuration is the poll interval while a PR has pending/in-progress checks.

func (PRWatchConfig) PollTerminalDuration added in v0.59.0

func (p PRWatchConfig) PollTerminalDuration() time.Duration

PollTerminalDuration is the poll interval once all checks are terminal (PR still open).

func (PRWatchConfig) RefDebounceDuration added in v0.69.1

func (p PRWatchConfig) RefDebounceDuration() time.Duration

RefDebounceDuration coalesces a burst of ref writes into one kick. Default 750ms.

func (PRWatchConfig) RefReconcileIntervalDuration added in v0.69.1

func (p PRWatchConfig) RefReconcileIntervalDuration() time.Duration

RefReconcileIntervalDuration is the git-ref watcher reconcile cadence. Default 2s; an unset, unparseable, or non-positive value uses the default (the ref watcher feeds this straight to time.NewTicker, which panics on a non-positive interval).

func (PRWatchConfig) TrustedAssociationSet added in v0.67.1

func (p PRWatchConfig) TrustedAssociationSet() map[string]bool

TrustedAssociationSet returns the resolved set of trusted author_association values as an upper-cased lookup set. A configured list is normalised to upper-case (GitHub returns the enum upper-cased, but config is hand-written) and empty/whitespace entries are dropped.

The nil vs present-but-empty distinction is load-bearing and fails CLOSED (issue #1039):

  • A NIL slice means "unset" (the Go zero value, or a config built without defaults) and falls back to DefaultTrustedAssociations. Load() seeds the field from default_config.toml, so an unset key in a real config resolves to the default three; nil here covers direct struct construction.
  • A PRESENT-but-empty slice (trusted_author_associations = []) is an explicit "trust no association" — allowlist-only mode — and is honoured as an empty set. go-toml/v2 decodes `= []` to a non-nil empty slice, so it is distinguishable from an absent key, and we must NOT silently widen it back to the default (that would fail open on an operator asking to lock the gate down).

func (PRWatchConfig) UntrustedAuthorPromptRate added in v0.69.1

func (p PRWatchConfig) UntrustedAuthorPromptRate() int

UntrustedAuthorPromptRate caps untrusted-author trust prompts per window. Default 5.

func (PRWatchConfig) UntrustedAuthorPromptWindowDuration added in v0.69.1

func (p PRWatchConfig) UntrustedAuthorPromptWindowDuration() time.Duration

UntrustedAuthorPromptWindowDuration is the trust-prompt rate window. Default 30m; an unset, unparseable, or non-positive value uses the default. This window bounds the security-sensitive rolling anti-flood cap on untrusted-author trust prompts (UntrustedAuthorPromptRate); a zero/negative value must not slip through, or it would prune every prior timestamp and disable the cap (issue #1304).

type PairRate added in v0.66.3

type PairRate struct {
	Count int
	Per   time.Duration
}

PairRate is a parsed pair_request_rate: Count events per Per duration.

func ParsePairRequestRate added in v0.66.3

func ParsePairRequestRate(s string) (PairRate, error)

ParsePairRequestRate parses a "<n>/<unit>" rate such as "5/min". The unit is one of sec/min/hour (with the aliases second/minute/hour). The count must be a positive integer. Any other shape is a hard error (fail-closed).

type Paths

type Paths struct {
	Profile        string
	AppName        string
	ConfigFile     string
	DataDir        string
	RuntimeDir     string
	SocketPath     string
	PIDFile        string
	StateFile      string
	HumanTokenFile string
	LogDir         string
	DaemonLog      string
	MessagesDB     string
	TodosDB        string
	TmpDir         string
}

func ResolvePaths

func ResolvePaths() (Paths, error)

func (Paths) EnsureDirs

func (p Paths) EnsureDirs() error

func (Paths) WithDataDir added in v0.21.0

func (p Paths) WithDataDir(dataDir string) Paths

type RemoteConfig added in v0.66.3

type RemoteConfig struct {
	// Enabled turns the remote listener on. Off by default; when false the rest
	// of the block is not validated so a disabled block never blocks startup.
	Enabled bool `toml:"enabled"`
	// Mode selects the transport: "tsnet" (embedded Tailscale via tsnet) or
	// "interface" (bind the host's existing tailnet interface IP).
	Mode string `toml:"mode"`
	// Hostname is the tsnet node name / MagicDNS label and the remote TLS
	// certificate name.
	Hostname string `toml:"hostname"`
	// Port is the TCP port the listener binds.
	Port int `toml:"port"`
	// AuthKeyFile is the path to a tsnet auth key (tsnet mode only).
	AuthKeyFile string `toml:"auth_key_file"`
	// Tags are the tsnet ACL tags applied to the node (tsnet mode only).
	Tags []string `toml:"tags"`
	// AllowTailnetUsers is the WhoIs allowlist (Gate 1). Entries are either a
	// tailnet user email or a "tag:"-prefixed tag. A bare "tag:" entry opts
	// tagged nodes in; with no tag entry, tagged nodes are disallowed.
	AllowTailnetUsers []string `toml:"allow_tailnet_users"`
	// RequirePairing requires per-device pairing (Gate 2) for human-level
	// rights. Defaults to true; false is UNSAFE (trusts the tailnet identity
	// alone) and is restricted to read-only access — see the design doc §B.2.
	RequirePairing bool `toml:"require_pairing"`
	// PairRequestRate is the anti-flood limit on pending pair requests, written
	// "<n>/<unit>" (e.g. "5/min"); units are sec, min, or hour. Empty falls back
	// to the pair_fallback_count/pair_fallback_window rate below.
	PairRequestRate string `toml:"pair_request_rate"`
	// MaxPendingPairings caps how many unapproved pair requests may be
	// outstanding at once (anti-flood). 0 uses the default
	// (RemoteMaxPendingPairingsDefault); values outside [1,
	// RemoteMaxPendingPairingsMax] are a hard config error.
	MaxPendingPairings int `toml:"max_pending_pairings"`
	// PendingPairingTTL is how long an unapproved pair request lives before it
	// expires and can no longer be approved. Empty uses the default
	// (RemotePendingPairingTTLDefault); a parsed value outside
	// [RemotePendingPairingTTLMin, RemotePendingPairingTTLMax] is a hard error.
	PendingPairingTTL string `toml:"pending_pairing_ttl"`
	// PairFallbackCount is the request count of the rate limit applied when
	// pair_request_rate is unset. 0 uses the default
	// (RemotePairFallbackCountDefault); values outside [1,
	// RemotePairFallbackCountMax] are a hard config error.
	PairFallbackCount int `toml:"pair_fallback_count"`
	// PairFallbackWindow is the window of the rate limit applied when
	// pair_request_rate is unset. Empty uses the default
	// (RemotePairFallbackWindowDefault); a parsed value outside
	// [RemotePairFallbackWindowMin, RemotePairFallbackWindowMax] is a hard error.
	PairFallbackWindow string `toml:"pair_fallback_window"`
}

RemoteConfig is the optional, off-by-default [remote] block that exposes a tailnet-facing control listener (see the native-app design doc §A.4/§B). It is fail-closed: when Enabled, an invalid block is a hard config-load error (static validation only — runtime listener provisioning failures, e.g. a missing tailnet IP or cert, are handled by the remote listener, not here).

func (RemoteConfig) AllowsTaggedNodes added in v0.66.3

func (r RemoteConfig) AllowsTaggedNodes() bool

AllowsTaggedNodes reports whether any allow_tailnet_users entry opts tagged nodes in (a "tag:"-prefixed entry). With no such entry, tagged nodes — which WhoIs resolves with no user — are disallowed by default.

func (RemoteConfig) MaxPendingPairingsOrDefault added in v0.69.1

func (r RemoteConfig) MaxPendingPairingsOrDefault() int

MaxPendingPairingsOrDefault returns the configured pending-pairing cap, applying the default when unset and clamping to the safe bounds so a caller that skipped Validate can never act on an unsafe value.

func (RemoteConfig) PairFallbackRate added in v0.69.1

func (r RemoteConfig) PairFallbackRate() PairRate

PairFallbackRate returns the rate limit applied when pair_request_rate is unset, applying defaults and clamping each component to its safe bounds.

func (RemoteConfig) PendingPairingTTLDuration added in v0.69.1

func (r RemoteConfig) PendingPairingTTLDuration() time.Duration

PendingPairingTTLDuration returns the configured pending-pairing TTL, applying the default when unset/unparseable and clamping to the safe bounds.

func (RemoteConfig) Validate added in v0.66.3

func (r RemoteConfig) Validate() error

Validate checks the [remote] block for static contradictions. Rules are only enforced when Enabled — a disabled block (even with otherwise-invalid values) always loads. It is fail-closed: an invalid enabled block is a hard error.

type RepoConfig added in v0.18.0

type RepoConfig struct {
	Path            string   `toml:"path"`
	AllowConcurrent bool     `toml:"allow_concurrent"`
	Singleton       bool     `toml:"singleton"`
	Includes        []string `toml:"includes"`
}

func (RepoConfig) Validate added in v0.19.0

func (rc RepoConfig) Validate() error

type ResourceMonitor added in v0.69.1

type ResourceMonitor struct {
	// SampleInterval is the cadence at which each session's process group is
	// snapshotted (and the per-session spacing that keeps a launch-burst kick
	// from replacing an established session's history). Empty, unparseable, or
	// non-positive uses the default (ResourceSampleIntervalDefault).
	SampleInterval string `toml:"sample_interval"`
	// SampleHistory is how many recent samples are retained per session (the
	// window shown in an abnormal-exit report). Values < 1 fall back to the
	// default (ResourceSampleHistoryDefault).
	SampleHistory int `toml:"sample_history"`
}

ResourceMonitor is the [resource_monitor] block controlling the daemon's per-session resource-sampling loop, which snapshots each live session's process-group RSS/CPU/FD usage (issue #1244). The values were previously fixed constants in internal/daemon/resource_monitor.go; every field is optional and falls back to the matching default constant.

func (ResourceMonitor) SampleHistoryOrDefault added in v0.69.1

func (r ResourceMonitor) SampleHistoryOrDefault() int

SampleHistoryOrDefault returns the retained-sample count, clamped to a sensible minimum. A non-positive value means "use the default".

func (ResourceMonitor) SampleIntervalDuration added in v0.69.1

func (r ResourceMonitor) SampleIntervalDuration() time.Duration

SampleIntervalDuration returns the sampling cadence, or the default when unset, unparseable, or non-positive (a zero cadence would busy-loop).

type SandboxConfig added in v0.11.0

type SandboxConfig struct {
	Enabled  bool  `json:"enabled"            toml:"enabled"`
	Disabled *bool `json:"disabled,omitempty" toml:"disabled,omitempty"`
	// Backend selects the sandbox backend: "safehouse" (macOS only) or "nono"
	// (Linux + macOS). It has NO default — when the sandbox is enabled and
	// Backend is unset the daemon fails closed with an actionable error. This
	// is a deliberate pre-1.0 behaviour change (see the nono sandbox design doc).
	Backend string `json:"backend,omitempty" toml:"backend"`
	Command string `json:"command,omitempty" toml:"command"`
	// Profile (nono only) is the base profile graith's generated profile
	// extends. Empty means nono's built-in "default" (its audited deny groups +
	// base system paths). Set it to a maintained registry profile — e.g.
	// "always-further/claude" — to inherit that agent's upstream file grants
	// (its ~/.claude, ~/.claude.json, versioned binary dir, …) instead of
	// hand-listing them via write_files.
	//
	// nono resolves "extends" by MERGING the base profile with graith's
	// generated one. Collection fields (filesystem.allow/read,
	// environment.allow_vars, network.allow_domain, …) are UNIONED (append +
	// dedup) — graith's grants are added to, not substituted for, the base's;
	// only scalar fields (e.g. workdir.access, security.signal_mode) are
	// child-overridden. So graith's filesystem grants are always present, but
	// graith's env allowlist can only WIDEN the base profile's, it cannot narrow
	// it. A base profile that allows extra env vars, network
	// domains, set_vars, command policies, or session hooks (which run outside
	// the sandbox) therefore relaxes graith's baseline — so a custom profile is
	// only as tight as the operator has audited it to be. Choose a trusted,
	// least-privilege profile. nono's audited deny groups (deny_credentials, …)
	// are marked required and merged into every resolved profile regardless of
	// this field, so a custom base cannot silently drop the credential-deny
	// baseline. The safehouse backend has no profile concept and ignores it.
	Profile   string   `json:"profile,omitempty"    toml:"profile"`
	Features  []string `json:"features,omitempty"   toml:"features"`
	ReadDirs  []string `json:"read_dirs,omitempty"  toml:"read_dirs"`
	WriteDirs []string `json:"write_dirs,omitempty" toml:"write_dirs"`
	// ReadFiles / WriteFiles grant access to individual files rather than whole
	// directories. They exist for paths that can't be expressed as a directory
	// grant without over-sharing — most importantly single files that live
	// directly in $HOME (e.g. an agent's ~/.claude.json login file), where
	// granting the parent directory would expose unrelated secrets (.env, ssh
	// keys, tfvars). ReadFiles is read-only; WriteFiles is read+write, mirroring
	// the read_dirs / write_dirs convention (where "write" means read+write, not
	// nono's write-only mode). They map to the nono profile's
	// filesystem.read_file / filesystem.allow_file; the safehouse backend folds
	// them into its read-only / read-write path lists.
	ReadFiles  []string `json:"read_files,omitempty"  toml:"read_files"`
	WriteFiles []string `json:"write_files,omitempty" toml:"write_files"`
	// SignalMode controls whether the sandboxed process may signal other
	// processes. It maps to nono's security.signal_mode ("isolated",
	// "allow_same_sandbox", "allow_all"). Empty inherits nono's base-profile
	// default (allow_same_sandbox). safehouse ignores it. Setting "isolated"
	// makes graith's `process-control` semantics meaningful under nono (Phase 1
	// left it a no-op). See the nono sandbox design doc §C5.
	SignalMode string `json:"signal_mode,omitempty" toml:"signal_mode"`
	// Network is an optional egress policy. It maps to the nono profile's
	// network section (network.block / network.allow_domain). safehouse has no
	// network primitive and only warns. A network policy also raises the
	// enforcement floor: nono needs Landlock ABI v4 (kernel 6.7+) to filter
	// network, so a requested policy on an older kernel fails closed.
	Network *SandboxNetworkConfig `json:"network,omitempty" toml:"network"`
}

func (SandboxConfig) Merge added in v0.11.0

func (s SandboxConfig) Merge(agent SandboxConfig) SandboxConfig

type SandboxNetworkConfig added in v0.64.0

type SandboxNetworkConfig struct {
	// Block denies all outbound network access (nono is network-allowed by
	// default). Maps to network.block = true.
	Block bool `json:"block,omitempty" toml:"block"`
	// AllowDomains is the proxy allowlist. Maps to network.allow_domain. When
	// set, nono runs its L7 filtering proxy and only these domains are
	// reachable. Entries are plain hostnames or URL globs.
	AllowDomains []string `json:"allow_domains,omitempty" toml:"allow_domains"`
}

SandboxNetworkConfig is graith's egress policy. It maps directly onto nono v0.66.0's profile network section: Block -> network.block, AllowDomains -> network.allow_domain (an L7 proxy allowlist; a plain hostname allows the host, a URL glob restricts to matching endpoints).

func (*SandboxNetworkConfig) IsSet added in v0.64.0

func (n *SandboxNetworkConfig) IsSet() bool

IsSet reports whether this network policy requests any egress restriction. A nil or empty config requests nothing (matches nono's allow-by-default).

type ScenarioLifecycleConfig added in v0.69.2

type ScenarioLifecycleConfig struct {
	Cleanup string `json:"cleanup,omitempty" toml:"cleanup"`
	Delay   string `json:"delay,omitempty"   toml:"delay"`
}

ScenarioLifecycleConfig controls optional cleanup after a completion epoch. The zero value is deliberately disabled for backward compatibility.

func (ScenarioLifecycleConfig) CleanupMode added in v0.69.2

func (c ScenarioLifecycleConfig) CleanupMode() string

func (ScenarioLifecycleConfig) DelayDuration added in v0.69.2

func (c ScenarioLifecycleConfig) DelayDuration() time.Duration

type ScheduleConfig added in v0.67.0

type ScheduleConfig struct {
	Cron     string `toml:"cron"`     // 5-field cron, or @hourly/@daily/@weekly/@monthly
	Every    string `toml:"every"`    // Go duration (supports "7d"): "15m", "1h30m"
	Timezone string `toml:"timezone"` // IANA zone for cron; default = daemon local time
}

ScheduleConfig is the time-driven source. Exactly one of Cron/Every is set.

type StatusBar added in v0.3.0

type StatusBar struct {
	Enabled  bool   `toml:"enabled"`
	Position string `toml:"position"`
}

type StatusConfig added in v0.32.0

type StatusConfig struct {
	TTL string `toml:"ttl"`
}

func (StatusConfig) TTLDuration added in v0.32.0

func (s StatusConfig) TTLDuration() time.Duration

type TemplateVars

type TemplateVars struct {
	Username                 string
	AgentSessionID           string
	SessionName              string
	SessionID                string
	WorktreePath             string
	ForkSourceAgentSessionID string
	Model                    string
	// Dir is the directory bound to {dir} when expanding an agent's
	// add_dir_args, once per granted worktree (see Agent.AddDirArgsFor). It is
	// empty in every other expansion context.
	Dir string
	// Profile, ReasoningEffort, ServiceTier, ApprovalPolicy, and WebSearch are
	// the Codex per-session options (issue #1186) surfaced as template variables
	// so an agent's conditional option_args groups (Agent.OptionArgsFor) can turn
	// them into CLI flags from config rather than hard-coded Go (issue #1236).
	// WebSearch is a boolean; it expands to "true" when set and "" otherwise, so
	// an option_args group can gate on it with `when = "web_search"`.
	Profile         string
	ReasoningEffort string
	ServiceTier     string
	ApprovalPolicy  string
	WebSearch       bool
}

type TerminalConfig added in v0.69.1

type TerminalConfig struct {
	// RefreshInterval is the cadence at which the session picker, the dashboard,
	// and an attached status bar re-poll the daemon for fresh session state.
	// Empty, unparseable, or non-positive uses the default
	// (TerminalRefreshIntervalDefault); a zero cadence would busy-loop.
	RefreshInterval string `toml:"refresh_interval"`
	// SummaryWidth is the maximum visible width (in cells) of a `gr status`
	// summary shown against a session in the picker before it is truncated with
	// an ellipsis. Values < 1 fall back to the default (TerminalSummaryWidth).
	SummaryWidth int `toml:"summary_width"`
}

TerminalConfig is the [terminal] block: user-tunable interactive-TUI presentation preferences that were previously fixed literals in the client (issue #1254) — how often the picker/dashboard/status bar refresh, and how wide a `gr status` summary may grow in the picker before truncation.

Session-lifecycle presentation (the fallback terminal geometry and the per-session scrollback cap) is deliberately NOT here: it lives in the [lifecycle] block (issue #1243, default_cols/default_rows/max_log_bytes), which owns the daemon's PTY seed. Layout invariants (the picker's column arithmetic, wrap widths, the minimum name column, and the GUI's frame rate) are also excluded — they must match render logic and stay as documented constants. Every field is optional and falls back to its default constant.

func (TerminalConfig) RefreshIntervalDuration added in v0.69.1

func (t TerminalConfig) RefreshIntervalDuration() time.Duration

RefreshIntervalDuration returns the TUI refresh cadence, or the default when unset, unparseable, or non-positive (a zero cadence would busy-loop).

func (TerminalConfig) SummaryWidthValue added in v0.69.1

func (t TerminalConfig) SummaryWidthValue() int

SummaryWidthValue returns the picker summary truncation width, or the default when the configured value is non-positive.

type TodoConfig added in v0.69.0

type TodoConfig struct {
	EmitEvents string `toml:"emit_events"` // "scenario" (default) | "all" | "off"
	ClaimLease string `toml:"claim_lease"` // Go duration; "" = 30m default; "0" disables
	Retention  string `toml:"retention"`   // Go duration; "" or "0" = keep done items forever
	// MaxTitle is the maximum todo title length in bytes. Values < 1 fall back to
	// the default (TodoMaxTitleDefault); a value above TodoMaxTitleCeiling — the
	// hard limit baked into the database CHECK constraint — is rejected at load.
	// Config may only tighten below the ceiling. Reloadable.
	MaxTitle int `toml:"max_title"`
	// MaxNote is the maximum todo note length in bytes. Values < 1 fall back to
	// the default (TodoMaxNoteDefault); a value above TodoMaxNoteCeiling — the
	// hard database CHECK ceiling — is rejected at load. Reloadable.
	MaxNote int `toml:"max_note"`
	// ListLimit caps how many items a single List/ListAll returns, so an in-scope
	// caller can't force an unbounded allocation or a long store-mutex hold. Values
	// < 1 fall back to the default (TodoListLimitDefault); a value above
	// TodoListLimitCeiling is rejected at load. Restart-only (fixed at store open).
	ListLimit int `toml:"list_limit"`
	// SweepInterval is how often the lease/retention sweep loop runs. Empty,
	// unparseable, or non-positive uses the default (TodoSweepIntervalDefault); a
	// zero cadence would busy-loop. Restart-only (the loop ticker is built once).
	SweepInterval string `toml:"sweep_interval"`
	// BusyTimeout is the SQLite busy_timeout for the todos database. The claim
	// contract ("loser gets zero rows") relies on a contended writer waiting rather
	// than erroring with SQLITE_BUSY, so this is load-bearing. Empty/unset uses the
	// default (TodoBusyTimeoutDefault, 5s). An explicit value must parse and fall
	// in [SQLiteBusyTimeoutResolution, TodoBusyTimeoutCeiling] (1ms–5m): SQLite's
	// busy_timeout has millisecond resolution, so a positive sub-1ms value would
	// collapse to busy_timeout(0) and disable the wait the claim contract depends
	// on, and it — along with any unparseable, non-positive, or above-ceiling
	// value — is rejected at load. Restart-only.
	BusyTimeout string `toml:"busy_timeout"`
}

TodoConfig is the [todo] block. It governs the first-class todo subsystem (issue #591): event emission on state change, the claim lease that reclaims stranded in-progress items, and the retention window that sweeps done items.

func (TodoConfig) BusyTimeoutDuration added in v0.69.1

func (t TodoConfig) BusyTimeoutDuration() time.Duration

BusyTimeoutDuration returns the todos-database SQLite busy_timeout, or the default when unset, unparseable, or non-positive (a zero/negative timeout would break the claim contract by failing a contended writer immediately).

func (TodoConfig) ClaimLeaseDuration added in v0.69.0

func (t TodoConfig) ClaimLeaseDuration() time.Duration

ClaimLeaseDuration resolves the claim-lease window. Unset (or, as a fail-safe, unparseable — though Validate rejects that at startup) defaults to 30m; an explicit "0" disables the lease sweep.

func (TodoConfig) EmitMode added in v0.69.0

func (t TodoConfig) EmitMode() string

EmitMode resolves the emit-events mode, defaulting to "scenario".

func (TodoConfig) ListLimitOrDefault added in v0.69.1

func (t TodoConfig) ListLimitOrDefault() int

ListLimitOrDefault returns the List/ListAll row cap. A non-positive value means "use the default".

func (TodoConfig) MaxNoteOrDefault added in v0.69.1

func (t TodoConfig) MaxNoteOrDefault() int

MaxNoteOrDefault returns the maximum note length. A non-positive value means "use the default".

func (TodoConfig) MaxTitleOrDefault added in v0.69.1

func (t TodoConfig) MaxTitleOrDefault() int

MaxTitleOrDefault returns the maximum title length. A non-positive value means "use the default".

func (TodoConfig) RetentionDuration added in v0.69.0

func (t TodoConfig) RetentionDuration() time.Duration

RetentionDuration resolves the done-item retention window. Unset or zero keeps done items indefinitely (returns 0).

func (TodoConfig) SweepIntervalDuration added in v0.69.1

func (t TodoConfig) SweepIntervalDuration() time.Duration

SweepIntervalDuration returns the lease/retention sweep cadence, or the default when unset, unparseable, or non-positive (a zero cadence would busy-loop the sweep timer).

type TokenAccounting added in v0.69.1

type TokenAccounting struct {
	// PollInterval is the cadence at which the loop re-derives per-session token
	// usage from transcripts. Empty, unparseable, or non-positive uses the
	// default (TokenPollIntervalDefault); a zero cadence would busy-loop.
	PollInterval string `toml:"poll_interval"`
	// StartupDelay is the short first-tick delay after a daemon (re)start so
	// `gr tokens` isn't blank for a full interval. Empty or unparseable uses the
	// default (TokenStartupDelayDefault); an explicit "0" polls immediately.
	StartupDelay string `toml:"startup_delay"`
	// BatchSize bounds how many sessions are (re)parsed per tick so a large fleet
	// with big transcripts can't stall the loop. Values < 1 fall back to the
	// default (TokenBatchSizeDefault).
	BatchSize int `toml:"batch_size"`
}

TokenAccounting is the [token_accounting] block controlling the daemon's per-session token-usage loop, which periodically re-derives token totals from each supported session's on-disk transcript (issue #1244). The values were previously fixed constants in internal/daemon/tokens.go; every field is optional and falls back to the matching default constant, preserving the historical behaviour.

func (TokenAccounting) BatchSizeOrDefault added in v0.69.1

func (t TokenAccounting) BatchSizeOrDefault() int

BatchSizeOrDefault returns the per-tick parse cap, clamped to a sensible minimum. A non-positive value means "use the default".

func (TokenAccounting) PollIntervalDuration added in v0.69.1

func (t TokenAccounting) PollIntervalDuration() time.Duration

PollIntervalDuration returns the token-poll cadence, or the default when unset, unparseable, or non-positive (a zero cadence would busy-loop).

func (TokenAccounting) StartupDelayDuration added in v0.69.1

func (t TokenAccounting) StartupDelayDuration() time.Duration

StartupDelayDuration returns the first-tick delay, or the default when unset or unparseable. An explicit "0" is honoured (poll immediately after start).

type ToolsConfig added in v0.69.1

type ToolsConfig struct {
	// Git is the git executable (default "git").
	Git string `toml:"git"`
	// GH is the GitHub CLI executable (default "gh").
	GH string `toml:"gh"`
	// GCX is the Grafana Cloud CLI executable used by gcx trigger sources
	// (default "gcx").
	GCX string `toml:"gcx"`
	// Shell runs notification and trigger commands as `<shell> -c <cmd>`
	// (default "sh").
	Shell string `toml:"shell"`
	// OSAScript is the macOS osascript executable used for desktop
	// notifications (default "osascript").
	OSAScript string `toml:"osascript"`
	// PS is the process-listing executable (default "/bin/ps").
	PS string `toml:"ps"`
	// Lsof is the open-files listing executable (default "/usr/sbin/lsof").
	Lsof string `toml:"lsof"`
}

ToolsConfig is the tools block overriding the external executables graith shells out to (issue #1238). Each field may be a bare command name resolved on PATH ("git", "hub") or an absolute/relative path to a specific binary ("/run/current-system/sw/bin/git"). An empty field keeps graith's built-in default (see tools.Defaults). This unblocks Nix/custom-PATH installs, wrapper binaries, and alternate shells. Only explicit overrides are validated at startup; unset defaults retain plain PATH-lookup semantics.

func (ToolsConfig) Resolved added in v0.69.1

func (t ToolsConfig) Resolved(baseDir string) tools.Config

Resolved converts the config block into the tools package's Config, normalizing each path-valued override against baseDir (the directory holding config.toml). Empty fields are left empty here; tools.Configure fills them from tools.Defaults so there is a single source of default values. Because normalization produces a stable absolute path, the same value is used for validation and for execution regardless of the later exec.Cmd.Dir (#1293).

func (ToolsConfig) Validate added in v0.69.1

func (t ToolsConfig) Validate(baseDir string) error

Validate checks that every explicitly-set tool override resolves (a path exists and is executable; a bare name is found on PATH). Path-valued overrides are normalized against baseDir first, so a relative wrapper is validated at the exact location it will later be executed from. Unset fields are skipped so defaults keep PATH-lookup semantics.

type TrackerConfig added in v0.68.7

type TrackerConfig struct {
	Provider      string   `toml:"provider"`       // "github" (v1); "" defaults to github
	Repo          string   `toml:"repo"`           // resolves the tracker + is the spawn repo (required)
	ActiveState   string   `toml:"active_state"`   // open | closed | all (default open)
	ActiveLabels  []string `toml:"active_labels"`  // active iff the issue has one of these (empty = any state-matching issue)
	Assignee      string   `toml:"assignee"`       // optional tracker assignee filter (e.g. "@me")
	Grace         string   `toml:"grace"`          // inactive this long before reaping; default 5m
	MaxConcurrent int      `toml:"max_concurrent"` // cap on live tracker sessions (0 = unlimited)
	Reap          string   `toml:"reap"`           // stop | delete | none (default stop)
	Limit         int      `toml:"limit"`          // max issues fetched per poll (default 50)
}

TrackerConfig configures a tracker action's poll + reconcile behaviour. The spawned sessions' agent/model/prompt come from the enclosing ActionConfig; this block is the tracker-specific part. See docs/design/2026-07-16-tracker-poll-action.md.

func (TrackerConfig) ActiveStateOr added in v0.68.7

func (t TrackerConfig) ActiveStateOr() string

ActiveStateOr returns the configured active state, defaulting to open.

func (TrackerConfig) GraceDuration added in v0.68.7

func (t TrackerConfig) GraceDuration() time.Duration

GraceDuration returns the reap grace window, defaulting to 5m.

func (TrackerConfig) LimitOr added in v0.68.7

func (t TrackerConfig) LimitOr() int

LimitOr returns the per-poll issue fetch cap, defaulting to 50.

func (TrackerConfig) ProviderOr added in v0.68.7

func (t TrackerConfig) ProviderOr() string

ProviderOr returns the configured provider, defaulting to github.

func (TrackerConfig) ReapMode added in v0.68.7

func (t TrackerConfig) ReapMode() string

ReapMode returns the configured reap policy, defaulting to stop.

func (TrackerConfig) RepoPath added in v0.68.7

func (t TrackerConfig) RepoPath() string

RepoPath returns the tracker repo canonicalised the same way ActionConfig.RepoPath treats a repo (see that method). Empty when unset.

type TranscriptConfig added in v0.69.1

type TranscriptConfig struct {
	// MaxContextBytes is the approximate size budget for the rendered migration/
	// fork context document; older turns are elided to fit. 0/negative uses
	// TranscriptMaxContextBytesDefault.
	MaxContextBytes int `toml:"max_context_bytes"`
	// MaxToolOutputBytes caps each rendered tool-output block. 0/negative uses
	// TranscriptMaxToolOutputBytesDefault.
	MaxToolOutputBytes int `toml:"max_tool_output_bytes"`
	// MaxLineBytes is the scanner buffer cap for a single transcript line while
	// reading turns or summing usage (large tool outputs / base64 exceed the
	// 64KiB default). 0/negative uses TranscriptMaxLineBytesDefault.
	MaxLineBytes int `toml:"max_line_bytes"`
	// MaxMetadataLineBytes is the scanner buffer cap for the small metadata-only
	// scans (Codex rollout cwd / session-id lookup). 0/negative uses
	// TranscriptMaxMetadataBytesDefault.
	MaxMetadataLineBytes int `toml:"max_metadata_line_bytes"`
}

TranscriptConfig is the [transcript] block tuning the on-disk agent-transcript reader and renderer used by migration and fork (issue #1250). Every field is optional: a zero/non-positive value falls back to the matching default.

func (TranscriptConfig) MaxContextBytesOrDefault added in v0.69.1

func (t TranscriptConfig) MaxContextBytesOrDefault() int

MaxContextBytesOrDefault returns the rendered-context byte budget, or the default when unset or non-positive.

func (TranscriptConfig) MaxLineBytesOrDefault added in v0.69.1

func (t TranscriptConfig) MaxLineBytesOrDefault() int

MaxLineBytesOrDefault returns the transcript-line scanner cap, or the default when unset or non-positive.

func (TranscriptConfig) MaxMetadataLineBytesOrDefault added in v0.69.1

func (t TranscriptConfig) MaxMetadataLineBytesOrDefault() int

MaxMetadataLineBytesOrDefault returns the metadata-scan scanner cap, or the default when unset or non-positive.

func (TranscriptConfig) MaxToolOutputBytesOrDefault added in v0.69.1

func (t TranscriptConfig) MaxToolOutputBytesOrDefault() int

MaxToolOutputBytesOrDefault returns the per-tool-output cap, or the default when unset or non-positive.

type TriggerConfig added in v0.67.0

type TriggerConfig struct {
	Name       string            `toml:"name"`
	Enabled    *bool             `toml:"enabled"`    // nil => default true; explicit false disables
	Schedule   *ScheduleConfig   `toml:"schedule"`   // time-driven source
	Watch      *WatchConfig      `toml:"watch"`      // file-event source
	GCX        *GCXConfig        `toml:"gcx"`        // Grafana Cloud event source
	Completion *CompletionConfig `toml:"completion"` // scenario todo-completion edge
	Action     ActionConfig      `toml:"action"`
	Policy     TriggerPolicy     `toml:"policy"`
}

TriggerConfig is one [[trigger]] block. A trigger is (source) -> (action): exactly one of Schedule (#592), Watch (#593), GCX, or Completion is the source, and Action is what runs. Everything below the source line is shared between the source kinds. See docs/design/2026-07-11-triggers-design.md.

func (TriggerConfig) IsCompletion added in v0.69.2

func (t TriggerConfig) IsCompletion() bool

func (TriggerConfig) IsGCX added in v0.69.2

func (t TriggerConfig) IsGCX() bool

func (TriggerConfig) IsSchedule added in v0.67.0

func (t TriggerConfig) IsSchedule() bool

IsSchedule, IsWatch, IsGCX, and IsCompletion report the source kind.

func (TriggerConfig) IsWatch added in v0.67.0

func (t TriggerConfig) IsWatch() bool

func (TriggerConfig) TriggerEnabled added in v0.67.0

func (t TriggerConfig) TriggerEnabled() bool

TriggerEnabled reports whether the trigger is enabled (nil => true).

type TriggerPolicy added in v0.67.0

type TriggerPolicy struct {
	CatchUp   bool   `toml:"catch_up"`   // default false: never backfill missed fires
	Overlap   string `toml:"overlap"`    // "" or "skip" (default) | "allow" | "queue"(v2)
	RateLimit string `toml:"rate_limit"` // "N/duration"; default "5/30m"
}

TriggerPolicy controls missed-run / overlap / rate-limit behaviour.

func (TriggerPolicy) OverlapMode added in v0.67.0

func (p TriggerPolicy) OverlapMode() string

OverlapMode returns the effective overlap policy (empty => skip).

func (TriggerPolicy) RateLimitParsed added in v0.67.0

func (p TriggerPolicy) RateLimitParsed() (int, time.Duration)

RateLimitParsed parses "N/duration" (e.g. "5/30m"), defaulting to 5 per 30m.

type TriggerVars added in v0.67.0

type TriggerVars struct {
	Name            string // trigger name
	Date            string // e.g. 2026-07-11
	Datetime        string // RFC3339
	FireTime        string // scheduled/observed fire instant (RFC3339)
	SessionName     string // watch source: the bound session
	WorktreePath    string // watch source: the bound session's worktree
	ChangedFiles    string // watch source: comma-separated changed paths (or "")
	ChangeCount     string // watch source: number of changed paths
	ScenarioID      string // completion source: owning scenario ID
	ScenarioName    string // completion source: owning scenario name
	CompletionEpoch string // completion source: monotonically increasing epoch
	// Tracker action: the issue a spawned session is seeded from. These are known
	// template tokens (they live in this shared struct), so they expand to the
	// empty string — not an error — outside a tracker prompt; a genuinely unknown
	// token still errors.
	IssueNumber string // e.g. "643"
	IssueTitle  string
	IssueBody   string
	IssueURL    string
	IssueLabels string // comma-separated label names (or "")
	// GCX source: stable, structured event metadata. Raw alert title, labels,
	// annotations, and subject are deliberately excluded because external alert
	// text is untrusted input to an autonomous agent.
	GCXEventID       string
	GCXEventKind     string
	GCXEventState    string
	GCXEventURL      string
	GCXTeamID        string
	GCXIntegrationID string
	GCXStartedAt     string
}

TriggerVars is the variable set available to trigger delivery/message templates. It is deliberately separate from TemplateVars (which is a fixed struct for agent-arg expansion) — trigger templates have their own tokens and must not silently accept agent-arg names. Like Expand, ExpandTrigger errors on an unknown {token}.

type TriggersAdvancedConfig added in v0.69.1

type TriggersAdvancedConfig struct {
	// SchedulerTick is the trigger scheduler loop cadence. Cron granularity is one
	// minute, so this only bounds sub-minute "every" intervals and dispatch
	// latency. Default 1s. Applied when the scheduler loop starts.
	SchedulerTick string `toml:"scheduler_tick"`
	// RunHistoryMax caps how many past runs each trigger retains in its persisted
	// history. Default 20.
	RunHistoryMax int `toml:"run_history_max"`
	// WatchReconcileInterval is how often file-watch bindings are reconciled
	// against live sessions (creating, tearing down, and retrying degraded
	// bindings). Default 2s. Applied when the file-watch loop starts.
	WatchReconcileInterval string `toml:"watch_reconcile_interval"`
	// WatchRetryBaseBackoff is the delay before the first retry of a degraded
	// file-watch binding (e.g. one that hit fs.inotify.max_user_watches).
	// Subsequent retries back off exponentially from here. Default 5s.
	WatchRetryBaseBackoff string `toml:"watch_retry_base_backoff"`
	// WatchRetryMaxBackoff caps the exponential degraded-binding backoff so a
	// persistently degraded binding keeps retrying periodically. Default 5m.
	WatchRetryMaxBackoff string `toml:"watch_retry_max_backoff"`
	// WatchBuiltinIgnores is the daemon-wide set of directories/patterns never
	// watched by any file-watch trigger (on top of git ignore rules and per-trigger
	// watch.ignore). Omitting the key uses DefaultWatchBuiltinIgnores; an explicit
	// empty list ([]) keeps only the mandatory ignores. ".git"/".git/" are always
	// ignored regardless of this list (a watched .git churns constantly and creates
	// a feedback loop).
	WatchBuiltinIgnores []string `toml:"watch_builtin_ignores"`
	// CommandOutputCap truncates a command action's captured output to this many
	// bytes before delivery, bounding notification size. Default 4096.
	CommandOutputCap int `toml:"command_output_cap"`
}

TriggersAdvancedConfig carries the advanced tuning for the trigger scheduler and the file-watch runtime. These were formerly hard-coded policy literals in the daemon (internal/daemon/trigger.go, filewatch.go, trigger_actions.go); they are surfaced here so an operator can tune scheduler latency, file-watch reconcile cadence, degraded-binding retry backoff, the always-ignored directory set, and the command-output cap without a rebuild. Every field is optional: an unset (zero/empty) value resolves to the documented default through the TriggersRuntime accessors, so leaving [triggers.advanced] out is a no-op.

type TriggersRuntime added in v0.67.0

type TriggersRuntime struct {
	MaxConcurrent int `toml:"max_concurrent"` // default 4
	// Advanced holds the low-level scheduler and file-watch tuning knobs (loop
	// cadence, run-history retention, degraded-binding backoff, the daemon-wide
	// watch ignore list, and the command-output cap). Every field is optional and
	// falls back to the historical default via the accessors below, so a config
	// that omits [triggers.advanced] behaves exactly as before. Expose these only
	// for operators who need to trade off detection latency, filesystem-watch
	// load, and notification size — the defaults suit ordinary use.
	Advanced TriggersAdvancedConfig `toml:"advanced"`
}

TriggersRuntime holds daemon-wide trigger settings ([triggers] table, distinct from the [[trigger]] array).

func (TriggersRuntime) CommandOutputCap added in v0.69.1

func (r TriggersRuntime) CommandOutputCap() int

CommandOutputCap is the command-action output truncation cap in bytes. Default 4096.

func (TriggersRuntime) MaxConcurrentOr added in v0.67.0

func (r TriggersRuntime) MaxConcurrentOr() int

MaxConcurrentOr returns the daemon-wide concurrency cap, defaulting to 4.

func (TriggersRuntime) RunHistoryMax added in v0.69.1

func (r TriggersRuntime) RunHistoryMax() int

RunHistoryMax is the per-trigger retained run-history length. Default 20.

func (TriggersRuntime) SchedulerTickDuration added in v0.69.1

func (r TriggersRuntime) SchedulerTickDuration() time.Duration

SchedulerTickDuration is the trigger scheduler loop cadence. Default 1s; an unset, unparseable, or non-positive value uses the default (the scheduler feeds this straight to time.NewTicker, which panics on a non-positive interval).

func (TriggersRuntime) WatchBuiltinIgnores added in v0.69.1

func (r TriggersRuntime) WatchBuiltinIgnores() []string

WatchBuiltinIgnores returns the daemon-wide watch ignore list. An omitted key (nil) resolves to DefaultWatchBuiltinIgnores; an explicit empty list ([]) is honored as "only the mandatory ignores", so a nil slice and a present-empty slice are NOT conflated (issue #1309). The daemon additionally always ignores ".git"/".git/" regardless of this list. A fresh copy is returned so callers cannot mutate the shared default slice, and a present-empty list is returned as a non-nil slice so consumers can distinguish it from an omitted policy.

func (TriggersRuntime) WatchReconcileIntervalDuration added in v0.69.1

func (r TriggersRuntime) WatchReconcileIntervalDuration() time.Duration

WatchReconcileIntervalDuration is the file-watch binding reconcile cadence. Default 2s; an unset, unparseable, or non-positive value uses the default (the file watcher feeds this straight to time.NewTicker, which panics on a non-positive interval).

func (TriggersRuntime) WatchRetryBaseBackoffDuration added in v0.69.1

func (r TriggersRuntime) WatchRetryBaseBackoffDuration() time.Duration

WatchRetryBaseBackoffDuration is the first-retry delay for a degraded file-watch binding. An unset, unparseable, or non-positive value uses the 5s default. The resolved delay is capped by WatchRetryMaxBackoffDuration so the effective bounds remain coherent when a directly-constructed config sets base > max.

func (TriggersRuntime) WatchRetryMaxBackoffDuration added in v0.69.1

func (r TriggersRuntime) WatchRetryMaxBackoffDuration() time.Duration

WatchRetryMaxBackoffDuration caps the exponential degraded-binding backoff. An unset, unparseable, or non-positive value uses the 5m default.

type UnknownKey added in v0.66.0

type UnknownKey struct {
	// Table is the dotted parent-table path, e.g. "agents.claude.sandbox".
	// Empty for top-level keys.
	Table string
	// Name is the unrecognised leaf key, e.g. "read_dir".
	Name string
	// Suggestion is the closest known key in the same table, or "" if none is
	// close enough to be worth a "did you mean".
	Suggestion string
}

UnknownKey is a config key that graith's schema does not recognise. It is a diagnostic aid (surfaced by `gr doctor`), not a load error: the runtime load stays lenient so an older daemon won't refuse a config written for a newer graith, and a typo silently drops the key rather than bricking startup. See issue #720.

func UnknownKeys added in v0.66.0

func UnknownKeys(path string) ([]UnknownKey, error)

UnknownKeys parses the TOML at path and reports keys that don't map to any field in the Config schema — typos (read_dir vs read_dirs), keys under the wrong table, or options from a newer graith than this binary. Unknown keys are never returned as an error; the returned error is only for a missing, unreadable, or unparseable file.

func (UnknownKey) FullKey added in v0.66.0

func (u UnknownKey) FullKey() string

FullKey renders the key with its table prefix, e.g. "sandbox.read_dir".

type UpdatesConfig added in v0.69.1

type UpdatesConfig struct {
	// Enabled turns the update check on. Defaults to true via the embedded
	// default config; set false to disable all update-check network I/O.
	Enabled bool `toml:"enabled"`
	// Repository is the "owner/repo" whose latest release is queried. Empty uses
	// the canonical d0ugal/graith repository.
	Repository string `toml:"repository"`
	// Interval is how often the check refreshes (cached between checks). Empty
	// uses the 1h default; must parse as a duration.
	Interval string `toml:"interval"`
	// Timeout bounds the release HTTP request. Empty uses the 5s default; must
	// parse as a duration.
	Timeout string `toml:"timeout"`
}

UpdatesConfig is the [updates] block controlling the GitHub release check (issue #1253). It makes the previously hard-coded checker configurable for downstream forks, packaged/offline deployments, and users who don't want network update checks. Enabled defaults to true (set in the embedded default config) to preserve the historical opt-out behaviour; the remaining fields fall back to the version package defaults when empty.

func (UpdatesConfig) IntervalDuration added in v0.69.1

func (u UpdatesConfig) IntervalDuration() time.Duration

IntervalDuration returns the configured cache cadence, or 0 when unset/invalid so the version package applies its own default.

func (UpdatesConfig) TimeoutDuration added in v0.69.1

func (u UpdatesConfig) TimeoutDuration() time.Duration

TimeoutDuration returns the configured HTTP timeout, or 0 when unset/invalid so the version package applies its own default.

type WatchConfig added in v0.67.0

type WatchConfig struct {
	Repo     string   `toml:"repo"`     // bind to sessions on this repo
	Role     string   `toml:"role"`     // bind to sessions with this scenario role
	Paths    []string `toml:"paths"`    // optional include globs (worktree-relative)
	Ignore   []string `toml:"ignore"`   // extra ignore globs (added to built-ins + .gitignore)
	Debounce string   `toml:"debounce"` // quiet-window; default 30s
}

WatchConfig is the file-event source. It is a POLICY selector (repo/role), never a literal live session name in config. Binds to matching sessions as they are created.

func (WatchConfig) DebounceDuration added in v0.67.0

func (w WatchConfig) DebounceDuration() time.Duration

DebounceDuration returns the watch debounce, defaulting to 30s.

type Watcher added in v0.3.0

type Watcher struct {
	// contains filtered or unexported fields
}

func NewWatcher added in v0.3.0

func NewWatcher(path string, onChange func(*Config) error, log *slog.Logger, debounce time.Duration) *Watcher

NewWatcher creates a config-file watcher. debounce is the quiet period after the last write before reloading; a non-positive value falls back to ConfigReloadDebounceDefault so callers can pass 0 to accept the default. An onChange error rejects the loaded generation and is logged as a reload failure rather than as a successful config reload.

func (*Watcher) Run added in v0.3.0

func (w *Watcher) Run(ctx context.Context) error

Jump to

Keyboard shortcuts

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