Documentation
¶
Overview ¶
Package daemon — Phase 2: Daemon lifecycle + auto-start machinery.
The functions in this file (DaemonSpec, DetectDaemon, StartDaemon, EnsureDaemon) provide the orchestration logic to detect an existing daemon, spawn one if needed (with cross-process flock election), and return control to the caller. The DaemonLifecycle type in lifecycle.go tracks active connections and reaps idle daemons.
Package design: pkg/daemon is intentionally lean — it depends only on stdlib, github.com/gofrs/flock, and pkg/envutil (stdlib-only env helper). No import of pkg/webui, pkg/agent, or pkg/agent_tools.
Package daemon provides client-side health monitoring for the sprout daemon.
This is the Phase-1 client-side health-detection mechanism for SP-136. The CLI-on-daemon feature (Phase 2+) will call MonitorDaemonHealth before or while using the daemon. The fallback callback should switch the caller to in-process execution. No wiring into cmd/agent_command.go is done yet — the mechanism is built as a standalone, well-tested package ready for Phase 2 integration.
Index ¶
- Constants
- Variables
- func DetectDaemon(ctx context.Context, spec DaemonSpec) (bool, error)
- func EnsureDaemon(ctx context.Context, spec DaemonSpec) (alreadyRunning bool, err error)
- func PreferOOMVictim() error
- func StartDaemon(ctx context.Context, spec DaemonSpec) error
- type AgentClient
- func (c *AgentClient) Close() error
- func (c *AgentClient) CreateSession(ctx context.Context, name string) (*SessionInfo, error)
- func (c *AgentClient) ExecuteTool(ctx context.Context, name string, args map[string]any, workDir string) (*ToolResult, error)
- func (c *AgentClient) ListSessions(ctx context.Context) ([]SessionInfo, error)
- func (c *AgentClient) Query(ctx context.Context, prompt, workDir string, opts QueryOptions) (string, error)
- func (c *AgentClient) StreamQuery(ctx context.Context, prompt, workDir string, opts QueryOptions, ...) error
- func (c *AgentClient) SwitchSession(ctx context.Context, sessionID string) (*SessionInfo, error)
- type AgentOp
- type AgentRequest
- type AgentResponse
- type AgentServer
- type AgentService
- type DaemonActivity
- type DaemonLifecycle
- type DaemonSpec
- type EmbeddingManagerService
- func (s *EmbeddingManagerService) BuildIndex(ctx context.Context, workspaceRoot string) (*embedding.IndexStats, error)
- func (s *EmbeddingManagerService) CheckDuplicates(ctx context.Context, workspaceRoot, filePath, content string) (*embedding.CheckDuplicatesResult, error)
- func (s *EmbeddingManagerService) Embed(ctx context.Context, text string) ([]float32, error)
- func (s *EmbeddingManagerService) EmbedBatch(ctx context.Context, texts []string) ([][]float32, error)
- func (s *EmbeddingManagerService) Meta(ctx context.Context) (string, int, string, error)
- func (s *EmbeddingManagerService) QuerySimilar(ctx context.Context, workspaceRoot, text string, topK int, threshold float32) ([]embedding.QueryResult, error)
- type EmbeddingServer
- type EmbeddingService
- type HealthMonitor
- type HealthOption
- func WithFailureThreshold(n int) HealthOption
- func WithFallbackFunc(fn func(reason string)) HealthOption
- func WithInterval(d time.Duration) HealthOption
- func WithLogger(logger *slog.Logger) HealthOption
- func WithTimeout(d time.Duration) HealthOption
- func WithWarningFunc(fn func(string)) HealthOption
- type HealthStatus
- type QueryOptions
- type SessionInfo
- type StreamEvent
- type ToolResult
Constants ¶
const DefaultFailureThreshold = 3
DefaultFailureThreshold is the default number of consecutive failures before the fallback callback is invoked (3).
const DefaultHealthInterval = 5 * time.Second
DefaultHealthInterval is the default interval between health checks (5s).
const DefaultHealthTimeout = 2 * time.Second
DefaultHealthTimeout is the default timeout for each individual health check (2s).
const DefaultRemoteSocketTimeout = 10 * time.Minute
DefaultRemoteSocketTimeout bounds a single remote operation. Agent runs can take a while, but a stuck daemon must not hang the CLI.
Variables ¶
var ErrDaemonDisabled = errors.New("daemon disabled via SPROUT_DAEMON=0")
ErrDaemonDisabled is returned by EnsureDaemon when SPROUT_DAEMON=0. Callers should treat this as "daemon disabled" and fall back to in-process execution.
Functions ¶
func DetectDaemon ¶
func DetectDaemon(ctx context.Context, spec DaemonSpec) (bool, error)
DetectDaemon checks whether a healthy daemon is already reachable.
It tries GET on DaemonURL first (HTTP TCP). If that fails and SocketPath is non-empty, it tries a Unix-domain socket connection. Healthy means HTTP 200 from the /health endpoint (matching the check in health.go).
func EnsureDaemon ¶
func EnsureDaemon(ctx context.Context, spec DaemonSpec) (alreadyRunning bool, err error)
EnsureDaemon ensures a healthy daemon is running. It uses the PID file as a flock-based election primitive: exactly one process spawns the daemon; competing processes wait for it to become healthy.
Election protocol (single-shot TryLock):
- Winner (TryLock == true): writes PID, spawns daemon via startDaemonInner, polls health, then releases the flock after the daemon is healthy.
- Loser (TryLock == false): does NOT call Unlock (never held the lock). Polls DetectDaemon every ~300ms up to StartTimeout. If the daemon becomes healthy → returns (false, nil). On timeout or ctx cancellation → returns an error.
The escape hatch: if SPROUT_DAEMON env is "0", returns ErrDaemonDisabled.
Returns (alreadyRunning=true, nil) if a daemon was already reachable before this call. Returns (alreadyRunning=false, nil) if this call started the daemon and it became healthy, or if another process started it and this call only waited for it.
The flock guards the election only, not the daemon's lifetime. A stale PID file from a crashed process is handled automatically because the OS releases the flock when the process exits.
func PreferOOMVictim ¶
func PreferOOMVictim() error
PreferOOMVictim raises this process's oom_score_adj so the kernel is more likely to choose it as an OOM victim over user-facing processes, which stay at the default 0. A long-lived background helper holding a large working set should sacrifice itself before the kernel takes a user-facing process. No-op on platforms without oom_score_adj; the error is non-fatal and callers should log and continue.
func StartDaemon ¶
func StartDaemon(ctx context.Context, spec DaemonSpec) error
StartDaemon spawns the daemon process (detached) and polls until it is healthy or StartTimeout elapses. The spawned daemon is NOT tied to the caller's context lifetime — it survives ctx cancellation and the caller exiting. Returns an error on timeout, ctx cancellation, or if the process fails to start.
Types ¶
type AgentClient ¶
type AgentClient struct {
// contains filtered or unexported fields
}
AgentClient is the CLI-side thin client for the daemon's agent socket (SP-136 P4). The daemon owns agent state, conversation history, and tool dispatch; the CLI renders responses. Falls back to in-process execution at the caller's discretion when the socket is unavailable.
func NewAgentClient ¶
func NewAgentClient(socketPath string) (*AgentClient, error)
NewAgentClient dials the daemon agent socket. Returns an error when the daemon is not reachable — callers then fall back to in-process execution.
func (*AgentClient) Close ¶
func (c *AgentClient) Close() error
Close closes the underlying connection.
func (*AgentClient) CreateSession ¶
func (c *AgentClient) CreateSession(ctx context.Context, name string) (*SessionInfo, error)
CreateSession creates a named session.
func (*AgentClient) ExecuteTool ¶
func (c *AgentClient) ExecuteTool(ctx context.Context, name string, args map[string]any, workDir string) (*ToolResult, error)
ExecuteTool invokes a tool on the daemon, scoped to the caller's workDir.
func (*AgentClient) ListSessions ¶
func (c *AgentClient) ListSessions(ctx context.Context) ([]SessionInfo, error)
ListSessions returns known sessions.
func (*AgentClient) Query ¶
func (c *AgentClient) Query(ctx context.Context, prompt, workDir string, opts QueryOptions) (string, error)
Query runs a one-shot query on the daemon and returns the final response. workDir is the caller's working directory, required so the daemon (a single long-lived process that may serve many different projects over its lifetime) scopes tool execution to the right one.
func (*AgentClient) StreamQuery ¶
func (c *AgentClient) StreamQuery(ctx context.Context, prompt, workDir string, opts QueryOptions, emit func(StreamEvent) error) error
StreamQuery is Query with streamed events instead of a single result. The call returns after the terminal "done"/"error" event.
func (*AgentClient) SwitchSession ¶
func (c *AgentClient) SwitchSession(ctx context.Context, sessionID string) (*SessionInfo, error)
SwitchSession activates an existing session.
type AgentRequest ¶
type AgentRequest struct {
ID string `json:"id"`
Op AgentOp `json:"op"`
Prompt string `json:"prompt,omitempty"`
// WorkDir is the caller's working directory for query, stream_query,
// and execute_tool ops. The daemon has no other way to know which
// project a one-shot query or tool call is for: it's a single
// long-lived process that may serve callers from many different
// directories over its lifetime. Required for those ops —
// AgentService implementations must scope tool execution to WorkDir
// rather than the daemon process's own (fixed, arbitrary) cwd.
WorkDir string `json:"work_dir,omitempty"`
Options *QueryOptions `json:"options,omitempty"`
SessionName string `json:"session_name,omitempty"`
SessionID string `json:"session_id,omitempty"`
Tool string `json:"tool,omitempty"`
ToolArgs map[string]any `json:"tool_args,omitempty"`
}
AgentRequest is a single protocol request.
type AgentResponse ¶
type AgentResponse struct {
ID string `json:"id"`
Error string `json:"error,omitempty"`
Sessions []SessionInfo `json:"sessions,omitempty"`
Session *SessionInfo `json:"session,omitempty"`
Result string `json:"result,omitempty"`
Tool *ToolResult `json:"tool,omitempty"`
}
AgentResponse is a single protocol response (non-streaming ops).
type AgentServer ¶
type AgentServer struct {
// SocketPath is the Unix socket path to listen on.
SocketPath string
// Service backs all operations.
Service AgentService
// Logger receives request/error logs.
Logger *slog.Logger
// Activity, when non-nil, is marked Begin/End around each request so the
// daemon idle reaper can see agent socket traffic.
Activity *DaemonActivity
// OnClose, when non-nil, is invoked inside Close() after the listener and
// conns are closed, before Close returns. It blocks until the service's
// teardown finishes — mirroring webui's waitForAgentTeardown so a daemon
// exiting doesn't race an in-flight embedding-store flush.
OnClose func()
// contains filtered or unexported fields
}
AgentServer serves the SP-136 P4 agent socket protocol.
func (*AgentServer) Close ¶
func (s *AgentServer) Close() error
Close stops the server and drops connections.
func (*AgentServer) Start ¶
func (s *AgentServer) Start(ctx context.Context) error
Start begins listening. Returns once bound.
func (*AgentServer) Wait ¶
func (s *AgentServer) Wait()
Wait blocks until the server stops accepting.
type AgentService ¶
type AgentService interface {
// ListSessions returns known sessions.
ListSessions(ctx context.Context) ([]SessionInfo, error)
// CreateSession creates a named session.
CreateSession(ctx context.Context, name string) (*SessionInfo, error)
// SwitchSession activates an existing session.
SwitchSession(ctx context.Context, sessionID string) (*SessionInfo, error)
// Query runs a one-shot query scoped to workDir and returns the final
// response. Implementations must not let query state (conversation
// history, workspace root) leak between calls with different workDir —
// each call may be for an unrelated project.
Query(ctx context.Context, prompt, workDir string, opts QueryOptions) (string, error)
// StreamQuery is Query with streamed events instead of a single result.
StreamQuery(ctx context.Context, prompt, workDir string, opts QueryOptions, emit func(StreamEvent) error) error
// ExecuteTool invokes a tool by name with args, scoped to workDir.
ExecuteTool(ctx context.Context, name string, args map[string]any, workDir string) (*ToolResult, error)
}
AgentService is the daemon-side capability for CLI-on-daemon (SP-136 P4).
type DaemonActivity ¶
type DaemonActivity struct {
// contains filtered or unexported fields
}
DaemonActivity tracks whether a daemon subsystem is in use so the idle reaper (cmd/daemon_idle.go) can see socket-server traffic it would otherwise be blind to.
Two signals feed Idle:
- Begin/End maintain an in-flight request counter; any request being served counts as activity no matter how long it runs.
- Touch (and Begin/End) record the last-activity timestamp, so a request that just completed still counts as activity until the window elapses — bursty callers don't get the daemon torn down between requests.
func NewDaemonActivity ¶
func NewDaemonActivity() *DaemonActivity
NewDaemonActivity returns an activity tracker with no activity recorded.
func (*DaemonActivity) End ¶
func (a *DaemonActivity) End()
End marks an in-flight request complete.
func (*DaemonActivity) Idle ¶
Idle reports whether the subsystem has no in-flight work and has seen no activity within window.
func (*DaemonActivity) Touch ¶
func (a *DaemonActivity) Touch()
Touch records that activity happened now.
type DaemonLifecycle ¶
type DaemonLifecycle struct {
// contains filtered or unexported fields
}
DaemonLifecycle manages the connection lifetime of a daemon process.
Connections are tracked via Add/Remove. When the last connection disconnects, a shutdown delay timer starts. If a new connection arrives before the timer fires, the timer is cancelled. When the timer fires (with no active connections), the StopFunc is invoked exactly once.
Typical flow:
lc := NewDaemonLifecycle(60*time.Second, func() error {
return daemonProcess.Kill()
})
lc.Add() // first connection
lc.Add() // second connection (count → 2)
lc.Remove() // one disconnects (count → 1)
lc.Remove() // last disconnect (count → 0, 60s timer starts)
lc.Add() // new connection (count → 1, timer cancelled)
lc.Close() // clean up when shutting down
func NewDaemonLifecycle ¶
func NewDaemonLifecycle(shutdownDelay time.Duration, stopFunc func() error) *DaemonLifecycle
NewDaemonLifecycle creates a lifecycle manager with the given shutdown delay and stop function. The stop function is called exactly once when the last connection disconnects and the delay expires.
func (*DaemonLifecycle) Add ¶
func (l *DaemonLifecycle) Add() int
Add registers a new active connection. Returns the new count. If the shutdown delay timer is pending (count was 0), the timer is stopped — the daemon stays alive.
func (*DaemonLifecycle) Close ¶
func (l *DaemonLifecycle) Close() error
Close stops the internal timer and releases resources. It is safe to call multiple times and does NOT invoke StopFunc (callers manage the daemon process lifecycle independently).
func (*DaemonLifecycle) Count ¶
func (l *DaemonLifecycle) Count() int
Count returns the current number of active connections.
func (*DaemonLifecycle) Remove ¶
func (l *DaemonLifecycle) Remove() int
Remove deregisters a connection. Returns the new count. If the count reaches 0, the shutdown delay timer starts.
func (*DaemonLifecycle) TimerActive ¶
func (l *DaemonLifecycle) TimerActive() bool
TimerActive reports whether the shutdown delay timer is currently pending (count is 0 and timer hasn't fired yet). Useful for tests to verify the timer was started correctly.
type DaemonSpec ¶
type DaemonSpec struct {
// DaemonURL is the HTTP base URL to reach the daemon (e.g.
// "http://127.0.0.1:56000").
DaemonURL string
// SocketPath is the Unix-domain socket path as an alternative to
// TCP. If set, DetectDaemon tries this socket when DaemonURL fails.
SocketPath string
// PIDFilePath is the path to the PID-file lock used for cross-process
// election. A flock on this file ensures exactly one process spawns
// the daemon.
PIDFilePath string
// StartTimeout limits how long StartDaemon and EnsureDaemon wait for
// the daemon to become healthy after spawning.
StartTimeout time.Duration
// ShutdownDelay is the idle timeout used by DaemonLifecycle: when
// the last connection disconnects, the daemon is stopped after this
// delay unless a new connection arrives.
ShutdownDelay time.Duration
// DaemonCommand is the command (args[0] is the binary) used to spawn
// the daemon process. Default: [executable-path, "agent", "-d"].
DaemonCommand []string
// Env holds extra KEY=VALUE environment variables applied to the
// spawned daemon process (merged over the parent's environment).
// Used e.g. to set SPROUT_DAEMON_IDLE_TIMEOUT so auto-started
// daemons reap themselves after an idle period.
Env []string
// LogPath is where the spawned daemon's stdout/stderr are redirected.
// If empty, output goes to os.DevNull.
LogPath string
}
DaemonSpec configures the daemon lifecycle (URL, socket, PID file, spawn command, and timeouts). Zero-value fields are replaced with defaults by DefaultDaemonSpec(); callers may override any field.
func DefaultDaemonSpec ¶
func DefaultDaemonSpec() DaemonSpec
DefaultDaemonSpec returns a DaemonSpec with all fields set to their conventional defaults. Callers may then override individual fields (e.g. SocketPath for test isolation).
type EmbeddingManagerService ¶
type EmbeddingManagerService struct {
// Acquire returns a manager for the workspace root, or an error when
// the workspace is refused (e.g. the index is not enabled). A nil
// manager with a nil error means no manager is available at all. The
// daemon wires this to embedding.AcquireManager with the workspace's
// config.
Acquire func(workspaceRoot string) (*embedding.EmbeddingManager, error)
// Release drops a reference taken by Acquire.
Release func(m *embedding.EmbeddingManager)
// contains filtered or unexported fields
}
EmbeddingManagerService adapts a shared *embedding.EmbeddingManager to the socket protocol. Manager acquisition is workspace-scoped: each workspace root resolves to the daemon's shared manager (sole writer per index).
func (*EmbeddingManagerService) BuildIndex ¶
func (s *EmbeddingManagerService) BuildIndex(ctx context.Context, workspaceRoot string) (*embedding.IndexStats, error)
BuildIndex implements EmbeddingService.
func (*EmbeddingManagerService) CheckDuplicates ¶
func (s *EmbeddingManagerService) CheckDuplicates(ctx context.Context, workspaceRoot, filePath, content string) (*embedding.CheckDuplicatesResult, error)
CheckDuplicates implements EmbeddingService.
func (*EmbeddingManagerService) EmbedBatch ¶
func (s *EmbeddingManagerService) EmbedBatch(ctx context.Context, texts []string) ([][]float32, error)
EmbedBatch implements EmbeddingService.
func (*EmbeddingManagerService) QuerySimilar ¶
func (s *EmbeddingManagerService) QuerySimilar(ctx context.Context, workspaceRoot, text string, topK int, threshold float32) ([]embedding.QueryResult, error)
QuerySimilar implements EmbeddingService.
type EmbeddingServer ¶
type EmbeddingServer struct {
// SocketPath is the Unix socket path to listen on.
SocketPath string
// Service backs all operations.
Service EmbeddingService
// Logger receives request/error logs.
Logger *slog.Logger
// Activity, when non-nil, is marked Begin/End around each request so the
// daemon idle reaper can see embedding socket traffic.
Activity *DaemonActivity
// contains filtered or unexported fields
}
EmbeddingServer serves the SP-136 P3 JSON-over-Unix-socket embedding protocol. The daemon owns the sole model copy and the sole writer per workspace index; CLI processes talk to this server instead of loading their own model.
func (*EmbeddingServer) Close ¶
func (s *EmbeddingServer) Close() error
Close stops the server, closes the listener, and drops all client connections.
func (*EmbeddingServer) Start ¶
func (s *EmbeddingServer) Start(ctx context.Context) error
Start begins listening and serving. It returns once the listener is bound (not when serving stops — use Wait). Callers should call Close to stop.
func (*EmbeddingServer) Wait ¶
func (s *EmbeddingServer) Wait()
Wait blocks until the server has stopped accepting (after Close or ctx cancellation).
type EmbeddingService ¶
type EmbeddingService interface {
// Meta returns provider identity (name, dimensions, model hash).
Meta(ctx context.Context) (name string, dims int, modelHash string, err error)
// Embed returns a vector for one text.
Embed(ctx context.Context, text string) ([]float32, error)
// EmbedBatch returns vectors for many texts (same order).
EmbedBatch(ctx context.Context, texts []string) ([][]float32, error)
// QuerySimilar queries the index for workspaceRoot.
QuerySimilar(ctx context.Context, workspaceRoot, text string, topK int, threshold float32) ([]embedding.QueryResult, error)
// BuildIndex builds the index for workspaceRoot.
BuildIndex(ctx context.Context, workspaceRoot string) (*embedding.IndexStats, error)
// CheckDuplicates checks content against the workspaceRoot index.
CheckDuplicates(ctx context.Context, workspaceRoot, filePath, content string) (*embedding.CheckDuplicatesResult, error)
}
EmbeddingService is the daemon-side capability the socket protocol serves. Implemented by EmbeddingManagerService, which adapts the daemon's shared EmbeddingManager (sole model copy, sole index writer, inference gate).
type HealthMonitor ¶
type HealthMonitor struct {
// contains filtered or unexported fields
}
HealthMonitor periodically pings the daemon's /health endpoint and tracks consecutive failures. On reaching the failure threshold, it invokes the fallback callback (e.g. to switch to in-process execution).
The monitor is safe for concurrent use: Start/Stop are idempotent, and ConsecutiveFailures returns the latest atomic value.
func MonitorDaemonHealth ¶
func MonitorDaemonHealth(ctx context.Context, baseURL string, fallback func(reason string)) *HealthMonitor
MonitorDaemonHealth creates a HealthMonitor with default settings, starts it, and returns it. The fallback callback is invoked when the failure threshold is reached. Warnings are logged via slog.
The returned monitor should be stopped (or its context cancelled) when the caller no longer needs monitoring. Example:
mon := MonitorDaemonHealth(ctx, "http://localhost:56000", func(reason string) {
// switch to in-process execution
})
defer mon.Stop()
func NewHealthMonitor ¶
func NewHealthMonitor(baseURL string, opts ...HealthOption) *HealthMonitor
NewHealthMonitor creates a monitor configured with the given baseURL and options. Default values: interval=5s, timeout=2s, threshold=3. The monitor does not start automatically — call Start(ctx).
func (*HealthMonitor) ConsecutiveFailures ¶
func (m *HealthMonitor) ConsecutiveFailures() int
ConsecutiveFailures returns the current consecutive-failure count (atomic read). Useful for tests to assert failure tracking.
func (*HealthMonitor) Start ¶
func (m *HealthMonitor) Start(ctx context.Context)
Start launches the health-check goroutine. It polls /health on the configured interval until ctx is cancelled or Stop() is called.
Start is idempotent: calling it twice is safe (the second call is a no-op). The startOnce field (not a local once) guarantees only ONE goroutine ever runs, so stopDone is closed exactly once.
func (*HealthMonitor) Stop ¶
func (m *HealthMonitor) Stop()
Stop signals the monitor goroutine to exit. It is safe to call multiple times (sync.Once) and does not block on the goroutine exiting.
func (*HealthMonitor) WaitStop ¶
func (m *HealthMonitor) WaitStop()
WaitStop blocks until the monitor goroutine has exited. Safe to call after Stop() or after the context passed to Start() is cancelled.
type HealthOption ¶
type HealthOption func(*HealthMonitor)
HealthOption configures a HealthMonitor.
func WithFailureThreshold ¶
func WithFailureThreshold(n int) HealthOption
WithFailureThreshold sets the number of consecutive failures before the fallback callback is invoked.
func WithFallbackFunc ¶
func WithFallbackFunc(fn func(reason string)) HealthOption
WithFallbackFunc sets a callback invoked when the failure threshold is reached. The callback receives a descriptive reason string.
func WithInterval ¶
func WithInterval(d time.Duration) HealthOption
WithInterval sets the polling interval between health checks.
func WithLogger ¶
func WithLogger(logger *slog.Logger) HealthOption
WithLogger sets a custom logger for health check output.
func WithTimeout ¶
func WithTimeout(d time.Duration) HealthOption
WithTimeout sets the timeout for each individual health check.
func WithWarningFunc ¶
func WithWarningFunc(fn func(string)) HealthOption
WithWarningFunc sets a callback invoked on each health check failure (before the threshold is reached).
type HealthStatus ¶
type HealthStatus struct {
Status string `json:"status"`
Port int `json:"port"`
Uptime string `json:"uptime"`
AgentAvailable bool `json:"agent_available"`
ActiveQueries int `json:"active_queries"`
}
HealthStatus is decoded from the JSON response of GET /health. Fields map 1:1 to the fields returned by the daemon's health endpoint. Unknown or missing fields are silently ignored (defensive decoding).
func CheckHealth ¶
CheckHealth performs a single GET against baseURL+"/health" with the given timeout. On HTTP 200 the JSON body is decoded into a HealthStatus and returned. Any other status code, network error, or JSON decode error returns a descriptive error.
Each check uses a FRESH connection (no keep-alive pooling). A pooled keep-alive connection to a server that has since exited masks the port state — the retry dial can hit a reused ephemeral port and return a stale "ok" — which makes daemon-gone detection (idle reap, crash) unreliable. Health checks are infrequent, so the dial cost is negligible.
Use http.NewRequestWithContext internally so the caller's context cancellation is respected.
type QueryOptions ¶ added in v0.17.20
type QueryOptions struct {
Persona string `json:"persona,omitempty"`
Provider string `json:"provider,omitempty"`
Model string `json:"model,omitempty"`
RiskProfile string `json:"risk_profile,omitempty"`
MaxIterations int `json:"max_iterations,omitempty"`
}
QueryOptions carries the calling CLI's session-scoped flag overrides for query/stream_query ops. Zero fields mean "use the daemon's own defaults".
type SessionInfo ¶
type SessionInfo struct {
ID string `json:"id"`
Name string `json:"name"`
CreatedAt string `json:"created_at,omitempty"`
Active bool `json:"active,omitempty"`
}
SessionInfo describes an agent session.
type StreamEvent ¶
type StreamEvent struct {
Type string `json:"type"` // "delta" | "tool" | "done" | "error"
Content string `json:"content,omitempty"`
Tool string `json:"tool,omitempty"`
Error string `json:"error,omitempty"`
}
StreamEvent is one chunk of a streaming query run.
type ToolResult ¶
ToolResult is the outcome of an ExecuteTool call.