daemon

package
v0.37.0 Latest Latest
Warning

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

Go to latest
Published: May 24, 2026 License: MIT Imports: 33 Imported by: 0

Documentation

Index

Constants

View Source
const CurrentStateVersion = 7

Variables

View Source
var ErrDaemonRunning = errors.New("daemon already running")

Functions

func AcquirePIDFile

func AcquirePIDFile(path string) error

func ExecUpgrade

func ExecUpgrade(manifestPath, configFile, clientExecPath string) error

func HandleConnection

func HandleConnection(ctx context.Context, conn net.Conn, sm *SessionManager, log *slog.Logger)

HandleConnection processes the frame protocol for a single client connection.

func IsGraithDaemon added in v0.16.5

func IsGraithDaemon(pid int) bool

func Listen

func Listen(sockPath string) (net.Listener, error)

func ReleasePIDFile

func ReleasePIDFile(path string)

func Run

func Run(cfg *config.Config, paths config.Paths, configFile, adoptFrom string) error

Run starts the daemon: acquires PID file, listens on the Unix socket, serves connections, and blocks until SIGTERM/SIGINT or an upgrade signal.

func SaveState

func SaveState(path string, state *State) error

func StopDaemon

func StopDaemon(pidFile string) error

func ValidateSessionName added in v0.29.2

func ValidateSessionName(name string) error

ValidateSessionName checks that a session name is safe for use in git branch names, shell commands, osascript, template expansion, and environment variables.

func WriteManifest

func WriteManifest(dir string, m *UpgradeManifest) (string, error)

Types

type CreationConfig added in v0.27.0

type CreationConfig struct {
	Agent         config.Agent         `json:"agent"`
	SandboxConfig config.SandboxConfig `json:"sandbox_config"`
}

CreationConfig captures the agent and sandbox configuration at session creation time so the overlay can detect when the live config has diverged.

type IncludedRepoState added in v0.19.0

type IncludedRepoState struct {
	RepoPath     string `json:"repo_path"`
	RepoName     string `json:"repo_name"`
	WorktreePath string `json:"worktree_path"`
	Branch       string `json:"branch"`
	BaseBranch   string `json:"base_branch"`
	// contains filtered or unexported fields
}

type MCPManager added in v0.23.0

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

MCPManager manages MCP server processes. Each proxy connection gets its own dedicated MCP server process, started lazily on connect.

func NewMCPManager added in v0.23.0

func NewMCPManager(cfg *config.Config, extraServers []config.MCPServerConfig, logDir string, log *slog.Logger) *MCPManager

NewMCPManager creates an MCPManager. extraServers are auto-injected servers (like the graith MCP server) that aren't in the config file but must be available for proxy connections.

func (*MCPManager) Connect added in v0.23.0

func (m *MCPManager) Connect(serverName, proxyID string) (*MCPProcess, error)

Connect starts a new MCP server process for the given proxy and returns the process's stdin (for writing JSON-RPC) and stdout reader (for reading JSON-RPC). The caller owns the I/O loop.

func (*MCPManager) Disconnect added in v0.23.0

func (m *MCPManager) Disconnect(proxyID string)

Disconnect kills the MCP server process for the given proxy.

func (*MCPManager) HasServer added in v0.23.0

func (m *MCPManager) HasServer(name string) bool

HasServer returns true if the named MCP server is configured.

func (*MCPManager) Reload added in v0.23.0

func (m *MCPManager) Reload(cfg *config.Config)

Reload updates the server config. Running processes for removed or changed servers are killed so proxies reconnect with the new config.

func (*MCPManager) Shutdown added in v0.23.0

func (m *MCPManager) Shutdown()

Shutdown kills all running MCP server processes.

type MCPProcess added in v0.23.0

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

MCPProcess represents a running MCP server process for a single proxy connection.

type Message

type Message struct {
	ID         string `json:"id"`
	Seq        int64  `json:"seq"`
	Stream     string `json:"stream"`
	SenderID   string `json:"sender_id"`
	SenderName string `json:"sender_name,omitempty"`
	Body       string `json:"body"`
	ThreadID   string `json:"thread_id,omitempty"`
	ReplyTo    string `json:"reply_to,omitempty"`
	CreatedAt  string `json:"created_at"`
}

type MsgStore

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

func NewMsgStore

func NewMsgStore(dbPath string) (*MsgStore, error)

func (*MsgStore) Ack

func (s *MsgStore) Ack(stream, subscriber string, upToSeq int64) error

func (*MsgStore) AckLatest

func (s *MsgStore) AckLatest(stream, subscriber string) error

func (*MsgStore) AckMessages added in v0.16.5

func (s *MsgStore) AckMessages(stream, subscriber string, seqs []int64) error

func (*MsgStore) Cleanup added in v0.3.0

func (s *MsgStore) Cleanup(maxAge time.Duration, maxPerStream int) (int64, error)

func (*MsgStore) Close

func (s *MsgStore) Close() error

func (*MsgStore) ListStreams

func (s *MsgStore) ListStreams(subscriber string, includeSystem bool) ([]StreamInfo, error)

func (*MsgStore) Publish

func (s *MsgStore) Publish(stream, senderID, senderName, body, threadID, replyTo string) (Message, error)

func (*MsgStore) Read

func (s *MsgStore) Read(stream, subscriber string, onlyUnread bool, threadID string) ([]Message, error)

func (*MsgStore) Subscribe

func (s *MsgStore) Subscribe(stream string) (chan Message, func())

func (*MsgStore) TotalUnread added in v0.3.0

func (s *MsgStore) TotalUnread(subscriber string) int

type Server

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

func NewServer

func NewServer(l net.Listener, handler func(ctx context.Context, conn net.Conn), log *slog.Logger) *Server

func (*Server) Serve

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

func (*Server) Shutdown

func (s *Server) Shutdown()

type SessionManager

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

SessionManager orchestrates PTY sessions, state persistence, and git worktrees.

func NewSessionManager

func NewSessionManager(cfg *config.Config, paths config.Paths, log *slog.Logger) *SessionManager

NewSessionManager creates a SessionManager with the given config and paths.

func (*SessionManager) AdoptSessions

func (sm *SessionManager) AdoptSessions(manifest *UpgradeManifest) error

func (*SessionManager) ClearAttachedClient

func (sm *SessionManager) ClearAttachedClient(sessionID string, conn net.Conn)

func (*SessionManager) ClearSummary added in v0.32.0

func (sm *SessionManager) ClearSummary(sessionID string) error

func (*SessionManager) Create

func (sm *SessionManager) Create(name, agentName, repoPath, baseBranch, prompt, model, parentID string, noRepo bool, shareWorktree string, agentHooks bool, inPlace, allowConcurrent bool, rows, cols uint16) (SessionState, error)

Create starts a new agent session, either in a git worktree, in-place in an existing repo, or as a standalone scratch session (when noRepo is true).

The method uses three-phase locking to avoid holding the daemon mutex during potentially blocking git/network operations (fetch, GitHub API calls, PTY spawn):

  1. Lock: validate, reserve session as StatusCreating, unlock
  2. Git setup and PTY spawn (no lock held)
  3. Lock: commit to StatusRunning, unlock

func (*SessionManager) Delete

func (sm *SessionManager) Delete(id string) error

Delete stops a session, removes its worktree/branch, and deletes state. Git teardown is attempted before removing the session from state; if teardown fails the session is kept for retry and the error is returned.

func (*SessionManager) DeleteWithChildren added in v0.20.0

func (sm *SessionManager) DeleteWithChildren(id string, excludeRoot bool) ([]string, error)

DeleteWithChildren deletes a session and all its transitive descendants. Git teardown is attempted before removing each session from state; sessions whose teardown fails are kept for retry. Returns the list of deleted session IDs and an error if any teardowns failed.

func (*SessionManager) Diagnostics added in v0.28.0

func (sm *SessionManager) Diagnostics() protocol.DiagnosticsMsg

Diagnostics collects runtime health data for gr doctor.

func (*SessionManager) Fork added in v0.10.0

func (sm *SessionManager) Fork(name, sourceSessionID string, rows, cols uint16) (SessionState, error)

Fork creates a new session that branches from an existing session's git state and uses the agent's fork_args to carry over the conversation history.

Uses three-phase locking like Create to avoid holding the mutex during git fetch and PTY spawn.

func (*SessionManager) Get

func (sm *SessionManager) Get(id string) (SessionState, bool)

Get returns a copy of a session state by ID.

func (*SessionManager) GetPTY

func (sm *SessionManager) GetPTY(id string) (*grpty.Session, bool)

GetPTY returns the live PTY session by ID.

func (*SessionManager) HandleHookReport added in v0.11.0

func (sm *SessionManager) HandleHookReport(sr protocol.StatusReportMsg)

HandleHookReport processes a status report from an agent hook, updating the in-memory hookReports map and the session's AgentStatus. This is the authoritative source of agent status when hooks are active.

func (*SessionManager) IsAttachedClient added in v0.16.5

func (sm *SessionManager) IsAttachedClient(sessionID string, conn net.Conn) bool

func (*SessionManager) KickAttachedClient

func (sm *SessionManager) KickAttachedClient(sessionID string)

func (*SessionManager) List

func (sm *SessionManager) List() []SessionState

List returns copies of all known session states.

func (*SessionManager) LoadState

func (sm *SessionManager) LoadState() error

LoadState reads persisted state from disk and reconciles dead processes.

func (*SessionManager) PendingApprovals added in v0.13.0

func (sm *SessionManager) PendingApprovals() []protocol.ApprovalInfo

PendingApprovals returns a snapshot of all current pending approvals.

func (*SessionManager) PrepareUpgrade

func (sm *SessionManager) PrepareUpgrade(listenerFd uintptr, configFile string) (*UpgradeManifest, error)

func (*SessionManager) ReloadConfig added in v0.3.0

func (sm *SessionManager) ReloadConfig() error

ReloadConfig loads the config from disk and swaps it in, logging what changed.

func (*SessionManager) Rename

func (sm *SessionManager) Rename(id, newName string) error

func (*SessionManager) RespondToApproval added in v0.13.0

func (sm *SessionManager) RespondToApproval(requestID, decision, reason string) error

RespondToApproval delivers a user decision to a waiting approval request.

func (*SessionManager) Restart added in v0.26.0

func (sm *SessionManager) Restart(id string, rows, cols uint16) (SessionState, error)

Restart stops a running session (or no-ops if already stopped) and resumes it, picking up the current agent and sandbox configuration.

func (*SessionManager) Resume

func (sm *SessionManager) Resume(id string, rows, cols uint16) (SessionState, error)

Resume restarts a stopped session using the agent's resume_args.

Uses two-phase locking: the GitHub username discovery happens before the lock, and the PTY spawn happens after releasing the lock to avoid blocking the daemon.

func (*SessionManager) RunDetectionLoop

func (sm *SessionManager) RunDetectionLoop(ctx context.Context)

RunDetectionLoop periodically scans PTY scrollback to detect agent status (active, needs approval, ready) for all running sessions.

func (*SessionManager) RunMessageCleanupLoop added in v0.3.0

func (sm *SessionManager) RunMessageCleanupLoop(ctx context.Context)

func (*SessionManager) SetAttachedClient

func (sm *SessionManager) SetAttachedClient(sessionID string, conn net.Conn, kick func(), sendCtrl func(string, any))

func (*SessionManager) SetMCPManager added in v0.23.0

func (sm *SessionManager) SetMCPManager(mm *MCPManager)

func (*SessionManager) SetMsgStore

func (sm *SessionManager) SetMsgStore(ms *MsgStore)

func (*SessionManager) SetSummary added in v0.32.0

func (sm *SessionManager) SetSummary(sessionID, text string, ttlSeconds int) error

func (*SessionManager) Star added in v0.31.0

func (sm *SessionManager) Star(id string) error

Rename changes the display name of a session.

func (*SessionManager) Stop

func (sm *SessionManager) Stop(id string) error

Stop sends SIGTERM to a session's process without removing the session or worktree.

func (*SessionManager) StopAll

func (sm *SessionManager) StopAll(ctx context.Context)

StopAll gracefully terminates all running sessions.

func (*SessionManager) StopWithChildren added in v0.30.0

func (sm *SessionManager) StopWithChildren(rootID string, excludeRoot bool) ([]string, error)

StopWithChildren stops all descendants of rootID. If excludeRoot is true, the root session itself is not stopped. Already-stopped sessions are skipped. Returns the list of session IDs that were actually stopped.

func (*SessionManager) SubmitApproval added in v0.13.0

SubmitApproval registers a pending approval and blocks until a decision is made, the context is cancelled (hook disconnect), or the configured timeout elapses. If localmost is configured, it is tried first.

func (*SessionManager) Unstar added in v0.31.0

func (sm *SessionManager) Unstar(id string) error

type SessionState

type SessionState struct {
	ID                     string                `json:"id"`
	ParentID               string                `json:"parent_id,omitempty"`
	Name                   string                `json:"name"`
	RepoPath               string                `json:"repo_path"`
	RepoName               string                `json:"repo_name"`
	WorktreePath           string                `json:"worktree_path"`
	Branch                 string                `json:"branch"`
	BaseBranch             string                `json:"base_branch"`
	Agent                  string                `json:"agent"`
	AgentSessionID         string                `json:"agent_session_id,omitempty"`
	Model                  string                `json:"model,omitempty"`
	Status                 SessionStatus         `json:"status"`
	AgentStatus            string                `json:"agent_status,omitempty"`
	StatusChangedAt        time.Time             `json:"status_changed_at"`
	IdleSince              *time.Time            `json:"-"`
	GitDirty               bool                  `json:"-"`
	GitUnpushed            int                   `json:"-"`
	HookModel              string                `json:"-"`
	HookToolName           string                `json:"-"`
	HookCostUSD            *float64              `json:"-"`
	HookContextPercent     *float64              `json:"-"`
	ExitCode               *int                  `json:"exit_code,omitempty"`
	PID                    int                   `json:"pid,omitempty"`
	Sandboxed              bool                  `json:"sandboxed,omitempty"`
	SandboxConfig          *config.SandboxConfig `json:"sandbox_config,omitempty"`
	SharedWorktree         bool                  `json:"shared_worktree,omitempty"`
	SharedWorktreeSourceID string                `json:"shared_worktree_source_id,omitempty"`
	InPlace                bool                  `json:"in_place,omitempty"`
	Includes               []IncludedRepoState   `json:"includes,omitempty"`
	AgentHooks             bool                  `json:"agent_hooks,omitempty"`
	ApprovalsEnabled       bool                  `json:"approvals_enabled,omitempty"` // deprecated: migrated to AgentHooks in v4
	Starred                bool                  `json:"starred,omitempty"`
	SummaryText            string                `json:"summary_text,omitempty"`
	SummarySetAt           *time.Time            `json:"summary_set_at,omitempty"`
	SummaryTTL             int                   `json:"summary_ttl,omitempty"`
	LastOutputAt           *time.Time            `json:"last_output_at,omitempty"`
	CreatedAt              time.Time             `json:"created_at"`
	LastAttachedAt         *time.Time            `json:"last_attached_at,omitempty"`
	CreationCfg            *CreationConfig       `json:"creation_config,omitempty"`
}

type SessionStatus

type SessionStatus string
const (
	StatusRunning  SessionStatus = "running"
	StatusStopped  SessionStatus = "stopped"
	StatusErrored  SessionStatus = "errored"
	StatusDeleting SessionStatus = "deleting"
	StatusCreating SessionStatus = "creating"
)

type State

type State struct {
	Version  int                      `json:"version"`
	Sessions map[string]*SessionState `json:"sessions"`
}

func LoadState

func LoadState(path string) (*State, error)

func NewState

func NewState() *State

func (*State) Reconcile

func (s *State) Reconcile()

type StreamInfo

type StreamInfo struct {
	Name     string `json:"name"`
	Total    int64  `json:"total"`
	Unread   int64  `json:"unread"`
	LatestAt string `json:"latest_at,omitempty"`
}

type UpgradeManifest

type UpgradeManifest struct {
	ListenerFd int              `json:"listener_fd"`
	ConfigFile string           `json:"config_file"`
	Profile    string           `json:"profile,omitempty"`
	Sessions   []UpgradeSession `json:"sessions"`
}

func ReadManifest

func ReadManifest(path string) (*UpgradeManifest, error)

type UpgradeSession

type UpgradeSession struct {
	ID  string `json:"id"`
	Fd  int    `json:"fd"`
	PID int    `json:"pid"`
}

Jump to

Keyboard shortcuts

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