daemon

package
v0.5.0 Latest Latest
Warning

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

Go to latest
Published: Jun 14, 2026 License: AGPL-3.0 Imports: 79 Imported by: 0

Documentation

Index

Constants

View Source
const (
	WorkflowStatusRunning       = "running"
	WorkflowStatusPaused        = "paused"
	WorkflowStatusBlocked       = "blocked"
	WorkflowStatusBudgetLimited = "budget_limited"
	WorkflowStatusComplete      = "complete"
)

Workflow run statuses. A run is "running" while executeWorkflow owns it, "paused" when interrupted (user cancel or daemon death — both resume the same way), "blocked" when a step error was routed or aborted the run, "budget_limited" once the budget tripped, and "complete" on normal end (completed runs are cleared from the session rather than kept around).

View Source
const CurrentConfigVersion = 1

CurrentConfigVersion is the expected version number for settings.json files. Bump this when the config format changes in a breaking way.

View Source
const FeatureReadAgentsMD = "read_agents_md"

FeatureReadAgentsMD enables loading AGENTS.md files into the system prompt.

View Source
const FeatureReadClaudeMD = "read_claude_md"

FeatureReadClaudeMD enables loading CLAUDE.md files into the system prompt.

View Source
const FeatureToolOrchestrator = "tool_orchestrator"

FeatureToolOrchestrator is the feature flag name for the tool orchestrator mode.

View Source
const StatusOKRecord = "ok"

StatusOKRecord is the job_status value marking a successful run record.

Variables

View Source
var (
	ErrStreamIdleTimeout = llm.ErrStreamIdleTimeout
	ErrThinkingStall     = llm.ErrThinkingStall
)

ErrStreamIdleTimeout / ErrThinkingStall — re-exported from llm so retry loops can `errors.Is(err, ErrThinkingStall)` without importing llm.

View Source
var ErrMaxTokens = errors.New("max_tokens")

ErrMaxTokens is returned when the LLM response was truncated due to the output token limit.

View Source
var ErrSessionBusy = errors.New("session busy")

ErrSessionBusy is returned by Attach when the requested session is already open in another connection (exclusive single-writer ownership). Callers can retry later (the owner may disconnect) or restore/attach a different session.

View Source
var ErrSessionNotFound = errors.New("session not found")

ErrSessionNotFound is returned by Attach when the daemon has no persisted record for the requested ID (e.g. it was lost in a daemon restart before its first flush). Callers should orphan the session rather than retry.

View Source
var ErrVersionMismatch = errors.New("version mismatch")

ErrVersionMismatch is returned by Connect/Attach when the daemon refused the session because the client and daemon builds differ. The wrapped message carries both versions and the remediation command.

Functions

func AllowedWritePaths

func AllowedWritePaths(cwd string) []string

AllowedWritePaths returns the paths the sandbox permits writing to, given the working directory. Useful for user-facing descriptions.

func CloseIdleHTTPConnections

func CloseIdleHTTPConnections()

CloseIdleHTTPConnections drops all pooled connections in the shared transport. Called between retries in AgentRunner.Send and streamWithRetry so a poisoned conn doesn't get reused on the next attempt.

func ExcludeTools

func ExcludeTools(tools []llm.ToolParam, names ...string) []llm.ToolParam

ExcludeTools removes tools with the given names from a tool list.

func FilterToolSchemas

func FilterToolSchemas(allowed []string) []llm.ToolParam

FilterToolSchemas returns only the tool schemas whose names appear in the allowed list, using the package-level default timeout bounds. Sessions that want the schemas to reflect their configured tool_timeouts should use FilterToolSchemasWithBounds instead. If allowed is nil, returns all tools.

func FilterToolSchemasWithBounds

func FilterToolSchemasWithBounds(allowed []string, def, max time.Duration) []llm.ToolParam

FilterToolSchemasWithBounds is the bounds-aware variant of FilterToolSchemas. The bash/glob_files timeout descriptions are rendered against the given floor and cap so the LLM sees the actual, configurable window instead of the hard-coded defaults.

func FormatEditDiff

func FormatEditDiff(oldStr, newStr string, lineOffset int) string

FormatEditDiff builds a structured side-by-side diff by running the system `diff -U3` command on the old and new strings and parsing its unified output. The result uses tagged rows consumed by renderDiffDetail in chat.go:

"H <text>\n"                  — header line (summary)
"C <leftN> <rightN> <text>\n" — context line present on both sides
"R <leftN> <text>\n"          — removed line (left side only)
"A <rightN> <text>\n"         — added line (right side only)

lineOffset is the 0-based line index of oldStr's first line within the real file, computed before the write so the numbers reflect actual file positions.

func GetToolSchema

func GetToolSchema(name string) *llm.ToolParam

GetToolSchema returns a single tool schema by name, or nil if not found.

func IsReadOnlyTool

func IsReadOnlyTool(name string) bool

IsReadOnlyTool returns true if the tool name is read-only (safe for planning).

func LandlockExecMain

func LandlockExecMain(argv []string)

LandlockExecMain is the entry point for the hidden `vixd landlock-exec` subcommand. cmd/vixd/main.go dispatches to it on argv[1]=="landlock-exec" before any normal startup happens — the helper is fork-light: no daemon, no telemetry, no socket. It applies Landlock to itself, then execve's the rest of argv.

Failures are fatal and printed to stderr (which the parent's exec.Cmd captures and surfaces back to the LLM via the bash tool).

func LoadCustomAgents

func LoadCustomAgents(dir string) map[string]SubagentConfig

LoadCustomAgents parses .vix/agents/*.md files into SubagentConfig entries.

func LogError

func LogError(format string, args ...any)

func LogInfo

func LogInfo(format string, args ...any)

func LogLLMCall

func LogLLMCall(
	model string,
	system []llm.SystemBlock,
	messages []llm.MessageParam,
	tools []llm.ToolParam,
	response *llm.Message,
)

LogLLMCall logs an LLM call and response to {logDir}/{datetime}.json using the provider-neutral types so every adapter produces the same log shape.

func LogWarn

func LogWarn(format string, args ...any)

func PatchSpawnAgentDescription

func PatchSpawnAgentDescription(tools []llm.ToolParam, customAgents map[string]SubagentConfig)

PatchSpawnAgentDescription updates the spawn_agent tool description in the given tool list based entirely on the loaded agent definitions.

func PersistAllowedDirectory

func PersistAllowedDirectory(configPath string, dirs []string) error

PersistAllowedDirectory appends directories to the allowed_directories list in a settings.json file. Uses map[string]any for round-trip safety so that unknown fields are preserved.

func ProtectDaemon

func ProtectDaemon()

ProtectDaemon lowers the current process's OOM score so the kernel prefers killing child processes (which should be set to 1000) first.

func ReadOnlyToolSchemas

func ReadOnlyToolSchemas() []llm.ToolParam

ReadOnlyToolSchemas returns only the read-only tool schemas (for plan exploration).

func RegisterBuiltinHandlers

func RegisterBuiltinHandlers(s *Server)

RegisterBuiltinHandlers registers ping, init, and force_init handlers.

func RegisterCredentialHandlers added in v0.4.3

func RegisterCredentialHandlers(s *Server)

RegisterCredentialHandlers wires the daemon-owned credential RPCs.

func RegisterLocalProviderHandlers added in v0.5.0

func RegisterLocalProviderHandlers(s *Server)

RegisterLocalProviderHandlers wires the local-provider discovery RPC.

func RegisterToolHandlers

func RegisterToolHandlers(s *Server)

func SandboxAvailable

func SandboxAvailable() bool

SandboxAvailable reports whether a sandbox mechanism was detected.

func SandboxName

func SandboxName() string

SandboxName returns a human-readable name for the active sandbox.

func SetClientVersion added in v0.5.0

func SetClientVersion(v string)

SetClientVersion records the process-wide client build version used by all subsequently created SessionClients. Call once at startup, before any connection is opened.

func SetLLMLogDir

func SetLLMLogDir(dir string)

SetLLMLogDir sets the directory for LLM call logs.

func SetTmpLogDir

func SetTmpLogDir(dir string)

SetTmpLogDir sets the directory used for daemon log files. Empty string restores the os.TempDir() default.

func SkillToolSchema added in v0.4.1

func SkillToolSchema() llm.ToolParam

SkillToolSchema returns the neutral schema for the `skill` tool, which loads a named skill's full instructions (and a listing of its bundled files) on demand — the second level of progressive disclosure. It is only added to a session's tool list when at least one skill is loaded.

func StartPprofServer

func StartPprofServer(ctx context.Context, port int)

StartPprofServer starts a pprof HTTP server on 127.0.0.1:<port>. Routes are registered by the net/http/pprof side-effect import. Blocks until ctx is cancelled; call in a goroutine.

func StartWebServer

func StartWebServer(ctx context.Context, s *Server, port int)

StartWebServer starts the local web UI HTTP server on 127.0.0.1:<port>. It blocks until srv.ListenAndServe returns (call in a goroutine).

func SummarizeToolInput

func SummarizeToolInput(name string, input map[string]any) string

SummarizeToolInput returns a one-line human summary of tool input.

func TmpLogDir

func TmpLogDir() string

TmpLogDir returns the configured daemon log directory, or os.TempDir() if SetTmpLogDir has not been called.

func ToolSchemas

func ToolSchemas() []llm.ToolParam

ToolSchemas returns the tool definitions in the provider-neutral llm.ToolParam shape, using the package-level default timeout bounds. Sessions that want bounds-aware descriptions should use ToolSchemasWithBounds instead.

func ToolSchemasWithBounds

func ToolSchemasWithBounds(def, max time.Duration) []llm.ToolParam

ToolSchemasWithBounds returns the neutral tool definitions with the bash/glob_files timeout descriptions rendered against the given floor and cap.

Passing zero for either value falls back to the package-level defaults.

func VfsEdit

func VfsEdit(cwd string, allowedDirs []string, homeVixDir, path, oldString, newString string, keepComments bool) (message string, lineOffset int, err error)

VfsEdit performs an edit on a VFS-managed file. It minifies the current file content, matches oldString against the minified representation, writes the new minified content back to disk, then runs the formatter to restore valid source.

Unlike editFileImpl, there is no fallback on failure — errors are surfaced directly.

func VfsRead

func VfsRead(cwd string, allowedDirs []string, path string, offset, limit *int, keepComments bool) (string, error)

VfsRead reads a file, optionally extracts a line range, then minifies via Tree-sitter. offset/limit are 1-based line numbers. nil means whole file. Falls back to returning raw content if minification is not supported.

func VfsWrite

func VfsWrite(cwd string, allowedDirs []string, homeVixDir, path, content string) (string, error)

VfsWrite writes minified content to a VFS-managed file, then runs the formatter to restore valid source. Creates parent directories if needed.

The caller is expected to provide content in the same minified form that read_minified_file returns. After writing, the language's formatter expands it back into properly formatted code.

Unlike writeFileImpl, there is no fallback on failure — errors are surfaced directly.

func WorkflowSignalToolSchema added in v0.4.5

func WorkflowSignalToolSchema() llm.ToolParam

WorkflowSignalToolSchema returns the workflow_signal tool definition. It is appended to a workflow agent step's tool list when the step declares "signal": true, and is intercepted by the workflow engine rather than the session tool dispatcher: the agent uses it to declare the run complete or blocked, and the workflow's next_steps route on $(workflow.signal.status).

Types

type AgentRunner

type AgentRunner struct {
	Config   SubagentConfig
	LLM      LLM
	Messages []llm.MessageParam
	System   []llm.SystemBlock
	Tools    []llm.ToolParam
	MaxTurns int

	// ToolTimeouts carries the parent session's configured tool-call floor/cap
	// so this runner's tool dispatches honour the same settings.json bounds as
	// the main agent. Populated at construction in NewAgentRunner; zero values
	// fall back to package defaults in the dispatcher.
	ToolTimeouts ToolTimeouts

	// Per-Send() accumulated usage (reset at start of each Send call)
	LastInputTokens         int64
	LastOutputTokens        int64
	LastCacheCreationTokens int64
	LastCacheReadTokens     int64
	LastElapsed             time.Duration
	// contains filtered or unexported fields
}

AgentRunner is a persistent agent with maintained history.

func NewAgentRunner

func NewAgentRunner(config SubagentConfig, cred config.Credential, parentModel, cwd string, plugins PluginSource, toolTimeouts ToolTimeouts, searchDirs ...string) (*AgentRunner, error)

NewAgentRunner creates a persistent agent for a workflow. searchDirs is the ordered set of .vix root directories to resolve system prompt includes from, in precedence order (highest first). toolTimeouts carries the parent session's tool_timeouts bounds so the runner's tool dispatches honour the same settings.json floor/cap.

func (*AgentRunner) Clone

func (a *AgentRunner) Clone(cred config.Credential) (*AgentRunner, error)

Clone creates a deep copy of the agent runner (for fork_from).

func (*AgentRunner) Send

func (a *AgentRunner) Send(
	ctx context.Context,
	userPrompt string,
	executeTool func(name string, params map[string]any, cwd string) (*ToolResult, error),
	streamCallback func(delta string),
	cwd string,
	hooks *TurnHooks,
) (string, error)

Send sends a message to the agent, runs the LLM loop with tool dispatch, and returns the text output. Conversation history is preserved across calls.

type BackgroundTask

type BackgroundTask struct {
	ID     string
	Name   string
	Done   chan struct{}
	Result *SubagentResult
	// contains filtered or unexported fields
}

BackgroundTask tracks an in-flight or completed background subagent.

type BackgroundTaskRegistry

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

BackgroundTaskRegistry manages background subagent tasks.

func (*BackgroundTaskRegistry) Cancel

func (r *BackgroundTaskRegistry) Cancel(id string)

Cancel cancels a single in-flight background task by ID. No-op if the task is unknown or already finished.

func (*BackgroundTaskRegistry) CancelAll

func (r *BackgroundTaskRegistry) CancelAll()

CancelAll cancels every in-flight background task in the registry. Completed tasks are unaffected (their cancel funcs are no-ops).

func (*BackgroundTaskRegistry) Load

func (*BackgroundTaskRegistry) SpawnBackground

func (r *BackgroundTaskRegistry) SpawnBackground(
	ctx context.Context,
	config SubagentConfig,
	prompt string,
	cred vixconfig.Credential,
	parentModel string,
	plugins PluginSource,
	executeTool func(name string, params map[string]any, cwd string) (*ToolResult, error),
	cwd string,
	toolTimeoutDefault time.Duration,
	toolTimeoutMax time.Duration,
	searchDirs ...string,
) string

SpawnBackground launches a subagent in a goroutine and returns a task ID. The task gets its own cancellable context derived from the parent ctx, so it can be cancelled individually (via Cancel) or in bulk (via CancelAll) without affecting the parent.

func (*BackgroundTaskRegistry) Store

func (r *BackgroundTaskRegistry) Store(task *BackgroundTask)

func (*BackgroundTaskRegistry) WaitForTask

func (r *BackgroundTaskRegistry) WaitForTask(ctx context.Context, id string, timeout time.Duration) (*SubagentResult, error)

WaitForTask blocks until the task completes or the context is cancelled.

type BashJob

type BashJob struct {
	ID      string
	PID     int
	PGID    int
	LogPath string
	RCPath  string
	PIDPath string

	Done chan struct{} // closed after the reaper goroutine writes the rc file
	// contains filtered or unexported fields
}

BashJob is a single detached bash command spawned via bash tool `background: true`. The daemon keeps ownership of log/rc file handles and a cancel hook so the whole process group can be SIGKILLed at session shutdown (or when the job's own deadline fires). The LLM polls via ordinary `bash` calls against LogPath / RCPath — no new tool surface needed.

type BashJobRegistry

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

BashJobRegistry owns the bash-tool background jobs for one session. It does not cross sessions — a session shutdown reaps its own jobs via KillAll(). sync.Map because writes (Store on spawn, Delete on reap) are both O(jobs ever spawned) and reads are rare (only iteration in KillAll).

func (*BashJobRegistry) Delete

func (r *BashJobRegistry) Delete(id string)

func (*BashJobRegistry) KillAll

func (r *BashJobRegistry) KillAll()

KillAll cancels every job in the registry and waits up to 2s per job for its reaper goroutine to finish. Called from server.go when a session ends.

func (*BashJobRegistry) Load

func (r *BashJobRegistry) Load(id string) (*BashJob, bool)

func (*BashJobRegistry) Store

func (r *BashJobRegistry) Store(j *BashJob)

type BashStepTimeouts

type BashStepTimeouts struct {
	Default time.Duration
	Max     time.Duration
}

BashStepTimeouts is the resolved form of the bash_step_timeouts block, consumed by resolveBashStepTimeout when scheduling workflow bash steps.

type BudgetState added in v0.4.5

type BudgetState struct {
	TokensUsed     int64 `json:"tokens_used"`
	ElapsedSeconds int64 `json:"elapsed_seconds"`
}

BudgetState is the live usage accumulated by a run, persisted with it.

type Client

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

Client communicates with the vix daemon over a Unix socket. Used for one-shot commands (ping, brain context, etc.)

func NewClient

func NewClient(path string) *Client

NewClient creates a new daemon client.

func (*Client) DaemonVersion added in v0.5.0

func (c *Client) DaemonVersion() (string, error)

DaemonVersion returns the running daemon's build version, as reported by the ping handler. Daemons predating the version field report "" — callers must treat that as a mismatch with any released client.

func (*Client) DeleteProviderMethodKey added in v0.4.3

func (c *Client) DeleteProviderMethodKey(provider, methodID string) (CredStatus, error)

DeleteProviderMethodKey removes a provider method's stored key daemon-side.

func (*Client) DismissSession added in v0.5.0

func (c *Client) DismissSession(cwd, configDir, id string) error

DismissSession archives a persisted session record (open/ → closed/) without attaching it. Used to dismiss vix-initiated run records from the TUI.

func (*Client) ExecuteTool

func (c *Client) ExecuteTool(name string, params map[string]any, cwd string) (*ToolResult, error)

ExecuteTool sends a tool execution request to the daemon.

func (*Client) ExecuteToolConfirmed

func (c *Client) ExecuteToolConfirmed(name string, params map[string]any, cwd string) (*ToolResult, error)

ExecuteToolConfirmed re-sends a tool request with the confirmed flag.

func (*Client) ListSessions added in v0.4.2

func (c *Client) ListSessions(cwd, configDir string) ([]protocol.SessionSummary, error)

ListSessions returns the persisted open sessions for cwd, so the TUI can reopen them on launch. Sessions are stored globally (~/.vix/sessions) and filtered by cwd daemon-side.

func (*Client) LocalProviderStatus added in v0.5.0

func (c *Client) LocalProviderStatus() (map[string]LocalProviderState, error)

LocalProviderStatus probes the local providers daemon-side and returns the per-provider reachability + live model lists, keyed by provider id.

func (*Client) Ping

func (c *Client) Ping() bool

Ping checks if the daemon is running.

func (*Client) ProviderCredStatus added in v0.4.3

func (c *Client) ProviderCredStatus() (CredStatus, error)

ProviderCredStatus reads the credential status of all providers.

func (*Client) SetAuthToken

func (c *Client) SetAuthToken(token string)

SetAuthToken stores the shared-secret token used to authenticate every request. Must be called before any RPC if the daemon was started with -auth-token-path.

func (*Client) SetProviderAuthDefault added in v0.4.3

func (c *Client) SetProviderAuthDefault(provider, methodID string) (CredStatus, error)

SetProviderAuthDefault sets (methodID non-empty) or clears (empty) a provider's default credential method daemon-side.

func (*Client) StopDaemon added in v0.5.0

func (c *Client) StopDaemon() error

StopDaemon asks the daemon to perform a coordinated shutdown (every attached vix instance is told to quit, then the daemon exits). Exempt from the version gate so it works across mismatched builds.

func (*Client) StoreProviderMethodKey added in v0.4.3

func (c *Client) StoreProviderMethodKey(provider, methodID, key, baseURL string) (CredStatus, error)

StoreProviderMethodKey stores a provider method's key (and optional base URL) daemon-side and returns the refreshed status.

type Compaction added in v0.4.0

type Compaction struct {
	Threshold      float64 // (0,1]; default 0.8
	Auto           bool    // default true
	KeepLastNTurns int     // -1 = use ratio; >0 = keep exactly N trailing turns
	KeepRatio      float64 // default 0.25; used when KeepLastNTurns <= 0
}

Compaction is the resolved (validated, defaulted) form of the `compaction` block, stored on ProjectConfig and consumed by the auto-compaction logic and the /compact command in session.go.

type CredStatus added in v0.4.3

type CredStatus struct {
	Backend   string
	Providers map[string]config.ProviderAuthStatus
}

CredStatus bundles the per-provider credential statuses with the active storage backend ("keyring" | "file").

type HandlerFunc

type HandlerFunc func(data map[string]any) (map[string]any, error)

HandlerFunc is the type for daemon request handlers.

type InputDef

type InputDef struct {
	Description string `json:"description"`
}

InputDef declares an expected input parameter.

type InstanceClient added in v0.4.2

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

InstanceClient is a long-lived control connection that registers the running vix process as an "instance" with the daemon. The daemon counts these to know how many vix processes are attached (independently of sessions). The client sends a single instance.register command and then holds the connection open for the process lifetime; closing it (clean exit or process death) tells the daemon this instance is gone.

func RegisterInstance added in v0.4.2

func RegisterInstance(socketPath, authToken, mode string) (*InstanceClient, error)

RegisterInstance dials the daemon and registers this process as an attached instance, returning a handle whose Close ends the registration. mode is advisory ("tui" | "headless"). On any failure it returns an error and the caller may simply proceed without registration — the count is observability only (web UI vitals, logging).

func (*InstanceClient) Close added in v0.4.2

func (ic *InstanceClient) Close()

Close ends the instance registration, signalling the daemon that this process has detached.

type LLM

type LLM = llm.Client

LLM is the daemon-side alias for llm.Client. All callers use this type; the underlying adapter is provider-dependent (Anthropic, OpenAI, ...).

type LocalModel added in v0.5.0

type LocalModel struct {
	Spec        string `json:"spec"`         // prefixed, e.g. "ollama/qwen3:8b"
	DisplayName string `json:"display_name"` // bare server-side id
	// ContextWindow is the serving context in tokens (Ollama: the model's
	// trained context_length from /api/show; llama.cpp: n_ctx from /props).
	// 0 means unknown.
	ContextWindow int64 `json:"context_window,omitempty"`
	// Loaded marks a model currently held in memory (Ollama /api/ps).
	Loaded bool `json:"loaded,omitempty"`
}

LocalModel is one live-discovered model on a local provider's server.

type LocalProviderState added in v0.5.0

type LocalProviderState struct {
	Provider  string       `json:"provider"`
	BaseURL   string       `json:"base_url"`
	Reachable bool         `json:"reachable"`
	Models    []LocalModel `json:"models"`
}

LocalProviderState is the probe result for one local provider.

type PluginConfig

type PluginConfig = llm.PluginConfig

PluginConfig is the daemon-side alias for llm.PluginConfig. Kept as a type alias so the existing plugin loader code (which produces this type) works unchanged.

type PluginSource added in v0.5.0

type PluginSource = llm.PluginSource

PluginSource is the daemon-side alias for llm.PluginSource — the callback NewFromModel invokes at client-construction time to obtain the plugin config for a specific provider/model/credential.

func NewPluginSource added in v0.5.0

func NewPluginSource(dirs []string, version string) PluginSource

NewPluginSource returns a PluginSource that discovers and runs the executable plugin files in dirs every time an LLM client is built. Each plugin receives the target provider, model, and credential metadata (never the secret itself) as a JSON object on stdin — {"version", "model", "provider", "credential_source"} — so a plugin can emit "{}" for providers/credentials it does not target.

type ProjectConfig

type ProjectConfig struct {
	Agent              string
	AllowedDirectories []string
	DenyPaths          []string
	DenyURLs           []string
	Features           map[string]bool
	ToolTimeouts       ToolTimeouts
	BashStepTimeouts   BashStepTimeouts
	Compaction         Compaction
	MCPServers         []mcp.ServerConfig
}

ProjectConfig holds parsed values from settings.json.

func LoadProjectConfig

func LoadProjectConfig(configPaths ...string) ProjectConfig

LoadProjectConfig reads config from one or more paths (applied in order, later overrides earlier) and returns agent name, workflows, and features.

func (ProjectConfig) HasFeature

func (c ProjectConfig) HasFeature(name string) bool

HasFeature returns whether the named feature flag is enabled.

type ProviderCredEntry added in v0.4.3

type ProviderCredEntry struct {
	Provider string                    `json:"provider"`
	Status   config.ProviderAuthStatus `json:"status"`
}

ProviderCredEntry pairs a provider id with its credential status. Defined in the daemon package so the handler (producer) and Client method (consumer) share the wire shape without a protocol-package dependency.

type Server

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

Server is the Unix socket daemon server with a handler registry.

func NewServer

func NewServer(sockPath string, cred config.Credential, sessionID, model string, daemonConfig *config.DaemonConfig, plugins PluginSource) *Server

NewServer creates a new daemon server.

func (*Server) BroadcastEvent added in v0.4.3

func (s *Server) BroadcastEvent(ev protocol.SessionEvent)

BroadcastEvent pushes ev onto every live session's event channel (best-effort, non-blocking). Used to reach all attached TUIs at once — e.g. the coordinated quit-all that follows an in-app update.

func (*Server) EnableJobScheduler added in v0.5.0

func (s *Server) EnableJobScheduler()

EnableJobScheduler constructs the scheduled-jobs engine over the global job store (~/.vix/jobs). Must be called before ListenAndServe, which starts the timer loop; the config watcher hot-reloads the spec directory. No-op when the home directory is unavailable.

func (*Server) GetHandler

func (s *Server) GetHandler(command string) HandlerFunc

GetHandler returns the handler for the given command, or nil.

func (*Server) JobRunner added in v0.5.0

func (s *Server) JobRunner() jobs.Runner

JobRunner returns the jobs.Runner executing runs in-process: an isolated headless session per run, mirroring `vix -p [-w workflow]` semantics.

func (*Server) Jobs added in v0.5.0

func (s *Server) Jobs() []jobs.JobSnapshot

Jobs returns a snapshot of the scheduled jobs for external consumers (the web UI). Empty when the scheduler is disabled (no home directory / feature off).

func (*Server) ListenAndServe

func (s *Server) ListenAndServe(ctx context.Context) error

ListenAndServe starts the Unix socket server and blocks until ctx is cancelled.

func (*Server) LogAccess

func (s *Server) LogAccess(toolName string, params map[string]any)

LogAccess logs a tool access event. Safe to call even if accessDB is nil.

func (*Server) QuitAll added in v0.4.3

func (s *Server) QuitAll()

QuitAll tells every attached vix instance to quit, then shuts the daemon down. Invoked after an in-app update so all old processes exit and the freshly-installed binaries take effect on relaunch (brew keep_alive restart or vix auto-spawn). The short delay lets the per-session writer goroutines flush the quit event onto their sockets before the context is cancelled.

func (*Server) RegisterHandler

func (s *Server) RegisterHandler(command string, handler HandlerFunc)

RegisterHandler registers a handler for the given command.

func (*Server) Sessions

func (s *Server) Sessions() []SessionInfo

Sessions returns a snapshot of all currently active sessions.

func (*Server) SetUpdateStatus added in v0.4.3

func (s *Server) SetUpdateStatus(current, latest, url, method string)

SetUpdateStatus records the result of the daily release check so it can be emitted to every session at init. Safe for concurrent use.

func (*Server) SetVersion added in v0.5.0

func (s *Server) SetVersion(v string)

SetVersion records the daemon build version. Sessions started by clients whose version differs are refused (hard gate, see handleSession). Must be called before ListenAndServe. Leaving it unset (in-process test embeddings) disables the gate.

func (*Server) Shutdown

func (s *Server) Shutdown()

Shutdown gracefully closes all server resources.

func (*Server) Subscribe

func (s *Server) Subscribe() chan struct{}

Subscribe registers a new web-UI subscriber and returns a notification channel.

func (*Server) Unsubscribe

func (s *Server) Unsubscribe(ch chan struct{})

Unsubscribe removes a previously registered subscriber channel.

func (*Server) UpdateStatus added in v0.4.3

func (s *Server) UpdateStatus() (current, latest, url, method string)

UpdateStatus returns the last recorded release-check result.

func (*Server) Version added in v0.5.0

func (s *Server) Version() string

Version returns the daemon build version recorded via SetVersion.

type ServerVitals

type ServerVitals struct {
	CPUPercent   float64 `json:"cpu_percent"`
	CPUAvailable bool    `json:"cpu_available"`
	RAMUsed      uint64  `json:"ram_used"`
	RAMTotal     uint64  `json:"ram_total"`
	DiskUsed     uint64  `json:"disk_used"`
	DiskTotal    uint64  `json:"disk_total"`
}

ServerVitals holds a snapshot of host and daemon resource usage.

type Session

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

Session manages a single agent session over a persistent socket connection.

func NewSession

func NewSession(id string, server *Server, llmClient LLM, model, cwd, configDir string, forceInit bool, enableAutomaticWritePermission bool, enableAutomaticDirectoryAccess bool, headless bool, parentCtx context.Context) *Session

NewSession creates a new agent session.

func (*Session) AddUserMessage

func (s *Session) AddUserMessage(text string, attachments ...protocol.Attachment)

AddUserMessage appends a user message to the conversation, optionally with image attachments.

func (*Session) ReloadWorkflows added in v0.4.2

func (s *Session) ReloadWorkflows(wfs []*WorkflowDef)

ReloadWorkflows swaps in a freshly-loaded workflow list and re-emits event.workflows_available so the TUI refreshes its slash menu and Shift+Tab cycle live. Called by the daemon config watcher when config/workflow.json changes on disk. A workflow already mid-execution holds its own definition, so this only affects the list of *available* workflows.

func (*Session) Run

func (s *Session) Run()

Run is the main session loop. It initializes the brain, then waits for input.

func (*Session) RunExploration

func (s *Session) RunExploration(ctx context.Context, agentName, prompt string) (*SubagentResult, error)

RunExploration spawns a named agent (looked up from s.customAgents) as a foreground subagent and blocks until it completes or ctx is cancelled. It is intended for external callers such as the web API that need to run an agent on behalf of the session without going through the normal command loop.

type SessionClient

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

SessionClient manages a persistent connection to the daemon for agent sessions.

func NewSessionClient

func NewSessionClient(socketPath string) *SessionClient

NewSessionClient creates a new session client (does not connect yet).

func (*SessionClient) Attach added in v0.4.2

func (sc *SessionClient) Attach(cwd, configDir, model string, forceInit bool, enableAutomaticWritePermission bool, enableAutomaticDirectoryAccess bool, headless bool, attachSessionID string) error

Attach establishes a persistent connection and resumes the persisted session with the given ID. On success the daemon replays the conversation via event.replay. Returns ErrSessionNotFound when no record exists on disk.

func (*SessionClient) Close

func (sc *SessionClient) Close()

Close closes the underlying connection.

func (*SessionClient) Connect

func (sc *SessionClient) Connect(cwd, configDir, model string, forceInit bool, enableAutomaticWritePermission bool, enableAutomaticDirectoryAccess bool, headless bool) error

Connect establishes a persistent connection and starts an agent session.

func (*SessionClient) ConnectFork

func (sc *SessionClient) ConnectFork(cwd, configDir, model string, forceInit bool, enableAutomaticWritePermission bool, enableAutomaticDirectoryAccess bool, headless bool, forkSessionID string, forkTurnIdx int) error

ConnectFork establishes a persistent connection and starts a new agent session pre-seeded with the conversation history from forkSessionID up to and including the turn at forkTurnIdx (0-based).

func (*SessionClient) ReadEvent

func (sc *SessionClient) ReadEvent() (protocol.SessionEvent, error)

ReadEvent reads the next event from the daemon.

func (*SessionClient) SendCancel

func (sc *SessionClient) SendCancel() error

SendCancel cancels the current work.

func (*SessionClient) SendClose

func (sc *SessionClient) SendClose() error

SendClose ends the session.

func (*SessionClient) SendConfirm

func (sc *SessionClient) SendConfirm(approved bool, persistDirs bool) error

SendConfirm sends tool approval/denial.

func (*SessionClient) SendInput

func (sc *SessionClient) SendInput(text string, attachments []protocol.Attachment) error

SendInput sends user chat input with optional attachments.

func (*SessionClient) SendMarkRead added in v0.5.0

func (sc *SessionClient) SendMarkRead() error

SendMarkRead tells the daemon the user is viewing this session: the persisted unread flag is cleared. Sent by the TUI when a session gains focus and when a turn completes while focused.

func (*SessionClient) SendPlanAction

func (sc *SessionClient) SendPlanAction(action string, text string) error

SendPlanAction sends a plan review decision.

func (*SessionClient) SendSetModel

func (sc *SessionClient) SendSetModel(model string) error

SendSetModel requests that the daemon switch to a different LLM model.

func (*SessionClient) SendTrim

func (sc *SessionClient) SendTrim(turnIdx int) error

SendTrim instructs the daemon to trim the conversation history, keeping messages up to and including the turn at turnIdx (0-based).

func (*SessionClient) SendUpdateQuit added in v0.4.3

func (sc *SessionClient) SendUpdateQuit() error

SendUpdateQuit tells the daemon an in-app update finished installing: it broadcasts a quit to every attached vix instance and shuts itself down so the freshly-installed binaries take effect on relaunch.

func (*SessionClient) SendUserAnswer

func (sc *SessionClient) SendUserAnswer(answer string, text string) error

SendUserAnswer sends the user's answer to a question. The text parameter carries additional user input when has_user_input is used.

func (*SessionClient) SendUserAnswerBatch

func (sc *SessionClient) SendUserAnswerBatch(answers map[string]string) error

SendUserAnswerBatch sends batch answers (question ID → answer) for multi-question mode.

func (*SessionClient) SendWorkflow

func (sc *SessionClient) SendWorkflow(name, text string) error

SendWorkflow sends a workflow execution request with a prompt.

func (*SessionClient) SendWorkflowMessage

func (sc *SessionClient) SendWorkflowMessage(text string) error

SendWorkflowMessage enqueues a user message to be injected into the currently running workflow agent as soon as the current LLM turn ends.

func (*SessionClient) SessionID

func (sc *SessionClient) SessionID() string

SessionID returns the session ID assigned by the daemon.

func (*SessionClient) SetAuthToken

func (sc *SessionClient) SetAuthToken(token string)

SetAuthToken stores the shared-secret token used to authenticate every SessionCommand. Must be called before Connect if the daemon was started with -auth-token-path.

func (*SessionClient) StartedAt

func (sc *SessionClient) StartedAt() time.Time

StartedAt returns the time the daemon session was created.

type SessionInfo

type SessionInfo struct {
	ID            string  `json:"id"`
	CWD           string  `json:"cwd"`
	InputTokens   int64   `json:"input_tokens"`
	OutputTokens  int64   `json:"output_tokens"`
	StartedAt     string  `json:"started_at"`      // RFC3339
	LastRequestAt *string `json:"last_request_at"` // RFC3339, null if no request yet
	ParentID      string  `json:"parent_id,omitempty"`
	ForkTurnIdx   int     `json:"fork_turn_idx,omitempty"`
}

SessionInfo holds a snapshot of a live session for external consumers.

type SignalState added in v0.4.5

type SignalState struct {
	Status string `json:"status,omitempty"` // "complete" or "blocked"
	Note   string `json:"note,omitempty"`
}

SignalState carries the last workflow_signal emitted by an agent step. It is cleared whenever a step with signal=true starts, so each signal is only visible to the routing decisions that immediately follow it.

type StepAgentState added in v0.4.5

type StepAgentState struct {
	Config   SubagentConfig     `json:"config"`
	Messages []llm.MessageParam `json:"messages"`
}

StepAgentState is the serializable snapshot of a step's AgentRunner: everything needed to rebuild it with NewAgentRunner plus its conversation.

type StepOption

type StepOption struct {
	Title        string    `json:"title"`
	Description  string    `json:"description"`
	Steps        []StepRef `json:"steps,omitempty"`
	HasUserInput bool      `json:"has_user_input,omitempty"`
}

StepOption is a structured option for tool steps using ask_question_to_user.

type StepRef

type StepRef struct {
	ID        string            `json:"id"`
	Params    map[string]string `json:"params,omitempty"`
	ExecuteIf string            `json:"execute_if,omitempty"`
}

StepRef is a structured reference to a workflow step with optional parameter mappings.

type StepResult

type StepResult struct {
	Output string            `json:"output"`
	Parsed map[string]any    `json:"parsed,omitempty"` // nil if json_output was false or parse failed
	Params map[string]string `json:"params,omitempty"` // input params received by this step
}

StepResult holds output from a completed workflow step.

type StreamOpts

type StreamOpts = llm.StreamOpts

StreamOpts is the daemon-side alias for llm.StreamOpts.

type SubagentConfig

type SubagentConfig struct {
	Name         string   `json:"name"`
	Description  string   `json:"description,omitempty"` // short description for LLM tool listing
	Model        string   `json:"model,omitempty"`       // empty = inherit parent model
	Effort       string   `json:"effort,omitempty"`      // "adaptive", "low", "medium", "high", "max", or "" (inherit)
	Tools        []string `json:"tools,omitempty"`       // tool name filter; nil = all tools
	MaxTurns     int      `json:"max_turns,omitempty"`   // 0 = default (20)
	MaxTokens    int      `json:"max_tokens,omitempty"`  // per-LLM-call output token cap; 0 = default (32768)
	SystemPrompt string   `json:"system_prompt,omitempty"`
}

SubagentConfig defines how a subagent behaves.

type SubagentResult

type SubagentResult struct {
	Output              string
	IsError             bool
	InputTokens         int64
	OutputTokens        int64
	CacheCreationTokens int64
	CacheReadTokens     int64
	Elapsed             time.Duration
}

SubagentResult holds the output of a completed subagent run.

func RunSubagent

func RunSubagent(
	ctx context.Context,
	config SubagentConfig,
	prompt string,
	cred vixconfig.Credential,
	parentModel string,
	plugins PluginSource,
	executeTool func(name string, params map[string]any, cwd string) (*ToolResult, error),
	cwd string,
	hooks *TurnHooks,
	toolTimeoutDefault time.Duration,
	toolTimeoutMax time.Duration,
	searchDirs ...string,
) (*SubagentResult, error)

RunSubagent executes a subagent with its own conversation, tools, and LLM instance. It blocks until the subagent completes or the context is cancelled. executeTool is called directly (in-process, no socket round-trip). searchDirs is the ordered set of .vix root directories to resolve system prompt includes from, in precedence order (highest first).

toolTimeoutDefault and toolTimeoutMax propagate the parent session's tool_timeouts bounds so tool calls made by the subagent honour the same floor/cap as the rest of the session. Passing zero for either falls back to package-level defaults (defaultToolTimeoutDefault / defaultToolTimeoutMax).

type ThinkingStallError

type ThinkingStallError = llm.ThinkingStallError

ThinkingStallError is the daemon-side alias for llm.ThinkingStallError.

type ToolResult

type ToolResult struct {
	Output            string
	IsError           bool
	NeedsConfirmation bool
	ToolName          string
	Params            map[string]any
	LineOffset        int
}

ToolResult holds the result of a tool execution from the daemon.

type ToolTimeouts

type ToolTimeouts struct {
	Default time.Duration
	Max     time.Duration
}

ToolTimeouts is the resolved (validated, defaulted) form of the tool_timeouts block, stored on ProjectConfig and consumed by the tool dispatcher in session.go.

type TurnHooks

type TurnHooks struct {
	OnStreamDelta   func(delta string)
	OnThinkingDelta func(delta string)
	OnStreamDone    func(inputTokens, outputTokens, cacheCreation, cacheRead, elapsedMs int64)
	OnToolCall      func(ev protocol.EventToolCall)
	OnToolResult    func(toolID, name string, input map[string]any, output string, isError bool)
	OnBeforeStream  func(cancel context.CancelFunc)
	// OnRetry is called when a retryable API error is about to be retried.
	// Mirrors session.streamWithRetry's event.retry emission so workflow-agent
	// retries become visible in the trajectory instead of only vixd.log.
	OnRetry func(attempt, maxRetries, waitSecs int, reason string)
	// OnThinkingStall is called when a thinking block exceeded its stall
	// timeout. The caller appends a nudge message and retries; this hook
	// lets the TUI surface the event.
	OnThinkingStall func(elapsedMs int64, summaryChars int)
}

TurnHooks provides typed callbacks for streaming events between LLM turns. All fields are optional — nil callbacks are skipped.

type WorkflowBudget added in v0.4.5

type WorkflowBudget struct {
	MaxTokens     int64    `json:"max_tokens,omitempty"`     // total tokens (input+output+cache write+cache read) across all steps
	MaxSeconds    int64    `json:"max_seconds,omitempty"`    // wall-clock seconds, accumulated across resumes
	MaxIterations int      `json:"max_iterations,omitempty"` // loop iterations (steps executed in the main chain)
	OnExceeded    *StepRef `json:"on_exceeded,omitempty"`    // step to route to when the budget trips (default: stop)
}

WorkflowBudget is the optional `budget` block on a workflow definition. Zero/absent fields mean "unlimited" for that dimension. When any limit is exceeded the engine routes to OnExceeded (or stops when absent) exactly once, with the run status set to budget_limited.

type WorkflowDef

type WorkflowDef struct {
	Name       string                     `json:"name"`
	EntryPoint StepRef                    `json:"entry_point"`
	Steps      map[string]WorkflowStepDef `json:"steps"`
	Summary    string                     `json:"summary,omitempty"`
	Budget     *WorkflowBudget            `json:"budget,omitempty"` // optional run budget (tokens/seconds/iterations)
	// DisplayInTUI controls whether the workflow appears in the TUI's
	// workflow switcher (Shift+Tab) and slash menu. Default true; internal
	// workflows (e.g. the shipped heartbeat one) set false. It does not
	// affect runnability — jobs and explicit invocations still work by name.
	DisplayInTUI *bool `json:"display_in_tui,omitempty"`
}

WorkflowDef is the parsed config for a workflow.

func LoadWorkflowsFile added in v0.4.2

func LoadWorkflowsFile(path string) []*WorkflowDef

LoadWorkflowsFile reads a config/workflow.json file and returns its validated workflow list, preserving file order. Returns nil on a missing file or parse error; individually invalid workflows are skipped with a log line. Duplicate names within the file are disambiguated by appending an index so the UI can tell them apart.

func (*WorkflowDef) ShowInTUI added in v0.5.0

func (w *WorkflowDef) ShowInTUI() bool

ShowInTUI reports whether the workflow should be listed in the TUI (absent display_in_tui defaults to true).

type WorkflowRun

type WorkflowRun struct {
	Def         *WorkflowDef
	StepAgents  map[string]*AgentRunner // step_id -> runner used
	StepResults map[string]*StepResult  // step_id -> result
	State       *WorkflowRunState       // live persisted position/accounting for this run
}

WorkflowRun tracks a running workflow.

type WorkflowRunState added in v0.4.5

type WorkflowRunState struct {
	Name         string                    `json:"name"`
	Status       string                    `json:"status"`
	Prompt       string                    `json:"prompt"`                // the original $(workflow.prompt)
	CurrentRef   *StepRef                  `json:"current_ref,omitempty"` // resume cursor: step about to execute
	Iteration    int                       `json:"iteration"`             // total iterations across resumes
	StepResults  map[string]*StepResult    `json:"step_results,omitempty"`
	StepAgents   map[string]StepAgentState `json:"step_agents,omitempty"`
	Budget       BudgetState               `json:"budget"`
	Signal       SignalState               `json:"signal"`
	BudgetRouted bool                      `json:"budget_routed,omitempty"` // OnExceeded already taken
	ErrorRouted  bool                      `json:"error_routed,omitempty"`  // an on_error route already taken
}

WorkflowRunState is the persisted position of a workflow run. It is snapshotted once per engine loop iteration so an interrupted run (user cancel or daemon restart) can resume from its cursor with all step results and per-step agent conversations intact.

func (*WorkflowRunState) Resumable added in v0.4.5

func (st *WorkflowRunState) Resumable() bool

Resumable reports whether an interrupted run can be continued. Completed runs are cleared rather than kept, so anything still stored that isn't actively running again is fair game.

type WorkflowStepDef

type WorkflowStepDef struct {
	Type        string              `json:"type"`                   // "agent", "tool", or "bash" (required)
	Effort      string              `json:"effort,omitempty"`       // "adaptive", "low", "medium", "high", "max"
	NextSteps   []StepRef           `json:"next_steps,omitempty"`   // next steps to execute (empty = end workflow)
	InputParams map[string]InputDef `json:"input_params,omitempty"` // declared input parameters for this step
	Tool        string              `json:"tool,omitempty"`         // tool name for type="tool"
	Agent       string              `json:"agent,omitempty"`        // agent name (loaded from .vix/agents/)
	ForkFrom    string              `json:"fork_from,omitempty"`    // fork from a prior step's agent
	Prompt      string              `json:"prompt,omitempty"`       // template, supports $() syntax
	Command     string              `json:"command,omitempty"`      // bash command for type="bash"
	Input       string              `json:"input,omitempty"`        // piped to stdin (supports $() expansion)
	Output      string              `json:"output,omitempty"`       // file path to write step text output
	DenyTools   []string            `json:"deny_tools,omitempty"`   // tools blocked from executing
	Stream      *bool               `json:"stream,omitempty"`       // nil defaults to true
	Silent      bool                `json:"silent,omitempty"`       // suppress all TUI events + vixd dispatch logs for this step
	JSONOutput  bool                `json:"json_output,omitempty"`  // parse LLM output as JSON for variable expansion
	DisplayKey  string              `json:"display_key,omitempty"`  // JSON key to extract as per-step display text
	Explanation string              `json:"explanation,omitempty"`  // user-facing explanation shown at step start
	Question    string              `json:"question,omitempty"`     // question text for tool steps
	Options     []StepOption        `json:"options,omitempty"`      // structured options for ask_question_to_user
	Category    string              `json:"category,omitempty"`     // tab/category label for ask_question_to_user
	TimeoutSec  *int                `json:"timeout_sec,omitempty"`  // per-step timeout (type="bash" only); pointer distinguishes absent from 0
	Signal      bool                `json:"signal,omitempty"`       // agent steps: expose the workflow_signal tool to the agent
	OnError     *StepRef            `json:"on_error,omitempty"`     // agent steps: route here instead of aborting when the step fails
}

WorkflowStepDef defines one step in the workflow.

func (*WorkflowStepDef) IsStreamVisible

func (s *WorkflowStepDef) IsStreamVisible() bool

IsStreamVisible returns whether streaming output should be shown for this step.

Directories

Path Synopsis
lsp
Package jobs implements vixd's scheduled-jobs engine: user-authored job specs (~/.vix/jobs/<id>.json) fired by a single timer loop, each run executing a prompt — optionally through a workflow — in an isolated session.
Package jobs implements vixd's scheduled-jobs engine: user-authored job specs (~/.vix/jobs/<id>.json) fired by a single timer loop, each run executing a prompt — optionally through a workflow — in an isolated session.
Package llm provides a provider-neutral interface for LLM interactions across Anthropic, OpenAI (Responses API), OpenRouter, MiniMax, and Xiaomi MiMo.
Package llm provides a provider-neutral interface for LLM interactions across Anthropic, OpenAI (Responses API), OpenRouter, MiniMax, and Xiaomi MiMo.

Jump to

Keyboard shortcuts

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