daemon

package
v0.1.1 Latest Latest
Warning

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

Go to latest
Published: Jun 25, 2026 License: MIT Imports: 26 Imported by: 0

Documentation

Overview

Package daemon provides a Telegram gateway for hawk. Allows users to interact with hawk via Telegram bot messages.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func SetVersion

func SetVersion(v string)

SetVersion propagates the canonical hawk version into the daemon.

Types

type ChatRequest

type ChatRequest struct {
	Prompt    string `json:"prompt"`
	SessionID string `json:"session_id,omitempty"`
	Model     string `json:"model,omitempty"`
	MaxTurns  int    `json:"max_turns,omitempty"`
	Autonomy  string `json:"autonomy,omitempty"`
	CWD       string `json:"cwd,omitempty"`
	Agent     string `json:"agent,omitempty"`
}

ChatRequest is the JSON body for POST /v1/chat.

type ChatResponse

type ChatResponse struct {
	SessionID  string `json:"session_id"`
	Response   string `json:"response"`
	TokensIn   int    `json:"tokens_in"`
	TokensOut  int    `json:"tokens_out"`
	TurnsTaken int    `json:"turns_taken"`
	Duration   string `json:"duration"`
}

ChatResponse is the JSON response from POST /v1/chat.

type Config

type Config struct {
	Port    int    `json:"port"`
	Host    string `json:"host"`
	LogFile string `json:"log_file"`
	APIKey  string `json:"api_key,omitempty"`
	// Gateways configures optional messaging bridges (Telegram/Discord/Slack).
	// All are disabled by default; the daemon starts normally when none are set.
	Gateways GatewaysConfig `json:"gateways,omitempty"`
}

Config holds daemon configuration.

func DefaultConfig

func DefaultConfig() Config

DefaultConfig returns reasonable defaults.

type DiscordConfig

type DiscordConfig struct {
	Enabled bool   `json:"enabled,omitempty"`
	Token   string `json:"token,omitempty"` // bot token
	// AppID is the application/bot user ID, used to detect @mentions.
	AppID       string   `json:"app_id,omitempty"`
	PairingCode string   `json:"pairing_code,omitempty"`
	AllowList   []string `json:"allow_list,omitempty"`
}

DiscordConfig configures the Discord gateway.

type DiscordGateway

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

DiscordGateway bridges hawk to Discord. Because no WebSocket library is present in go.mod, the gateway uses a REST long-poll-equivalent strategy: it does not open the Discord Gateway socket but instead relies on a poll over the bot's accessible channels for new @mentions/DMs. This keeps the bridge dependency-free while remaining bidirectional (it posts replies in-thread via the REST API).

The polling transport is intentionally minimal and pluggable: fetchMessages is a field so a real Gateway-WebSocket implementation (or a test) can be swapped in without changing the message-handling logic.

func (*DiscordGateway) Name

func (g *DiscordGateway) Name() string

Name implements Gateway.

func (*DiscordGateway) Start

func (g *DiscordGateway) Start(ctx context.Context) error

Start implements Gateway: poll watched channels until ctx is cancelled.

func (*DiscordGateway) Stop

func (g *DiscordGateway) Stop() error

Stop implements Gateway. Polling is driven by the Start context.

type ErrorResponse

type ErrorResponse struct {
	Error   string `json:"error"`
	Code    string `json:"code,omitempty"`
	Details string `json:"details,omitempty"`
}

ErrorResponse is the standard error envelope.

type Gateway

type Gateway interface {
	// Name returns a short identifier for the gateway (e.g. "telegram").
	Name() string
	// Start begins processing messages. It should block until ctx is cancelled
	// (long-poll gateways) or return promptly after registering handlers
	// (webhook gateways). Implementations must respect ctx cancellation.
	Start(ctx context.Context) error
	// Stop releases any resources held by the gateway. It is safe to call Stop
	// even if Start was never called or already returned.
	Stop() error
}

Gateway is a bidirectional messaging bridge between an external chat platform (Telegram, Discord, Slack, ...) and the hawk daemon. Implementations forward inbound messages to the daemon's /v1/chat endpoint and relay the reply back.

type GatewaysConfig

type GatewaysConfig struct {
	Telegram TelegramConfig `json:"telegram,omitempty"`
	Discord  DiscordConfig  `json:"discord,omitempty"`
	Slack    SlackConfig    `json:"slack,omitempty"`
}

GatewaysConfig groups the per-platform messaging bridge configuration. All gateways are disabled by default; a gateway only starts when its Enabled flag is set and its required credentials are present.

type HealthResponse

type HealthResponse struct {
	Status    string `json:"status"`
	Version   string `json:"version"`
	Uptime    string `json:"uptime"`
	Sessions  int    `json:"active_sessions"`
	StartedAt string `json:"started_at"`
}

HealthResponse is the JSON response from GET /v1/health.

type MessageResponse

type MessageResponse struct {
	Role       string      `json:"role"`
	Content    string      `json:"content,omitempty"`
	ToolUse    interface{} `json:"tool_use,omitempty"`
	ToolResult interface{} `json:"tool_results,omitempty"`
}

MessageResponse is a message in GET /v1/sessions/{id}/messages.

type ModelStatResp

type ModelStatResp struct {
	Model    string  `json:"model"`
	Requests int     `json:"requests"`
	CostUSD  float64 `json:"cost_usd"`
}

ModelStatResp is per-model statistics within StatsResponse.

type PaginatedResponse

type PaginatedResponse struct {
	Data    interface{} `json:"data"`
	Total   int         `json:"total"`
	Offset  int         `json:"offset"`
	Limit   int         `json:"limit"`
	HasMore bool        `json:"has_more"`
}

PaginatedResponse wraps paginated list results.

type Preheater

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

Preheater maintains warm connections and pre-initialized state to eliminate cold-start latency on first request.

func NewPreheater

func NewPreheater(interval time.Duration) *Preheater

NewPreheater creates a preheater with the given warmup interval.

func (*Preheater) Ready

func (p *Preheater) Ready() bool

Ready reports whether the preheater has completed at least one warmup cycle.

func (*Preheater) Start

func (p *Preheater) Start(endpoints []string)

Start begins background warmup. Call Stop() to clean up.

func (*Preheater) Stop

func (p *Preheater) Stop()

Stop terminates the background warmup goroutine.

func (*Preheater) Transport

func (p *Preheater) Transport() *http.Transport

Transport returns the pre-warmed HTTP transport for reuse.

type ReadyResponse

type ReadyResponse struct {
	// Ready is true only when every dependency is initialized.
	Ready bool `json:"ready"`
	// Reason describes why the daemon is not ready (empty when ready).
	Reason string `json:"reason,omitempty"`
	// Uptime is the time since the server started.
	Uptime string `json:"uptime"`
}

ReadyResponse is the JSON response from GET /v1/ready.

type ReviewRequest

type ReviewRequest struct {
	SHA        string `json:"sha"`
	Background bool   `json:"background,omitempty"`
	Model      string `json:"model,omitempty"`
	Concerns   string `json:"concerns,omitempty"`
}

ReviewRequest is the JSON body for POST /v1/review.

type ReviewResponse

type ReviewResponse struct {
	ID      int64  `json:"id"`
	SHA     string `json:"sha"`
	Status  string `json:"status"`
	Message string `json:"message,omitempty"`
}

ReviewResponse is the JSON response from POST /v1/review.

type Server

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

Server is the hawk daemon HTTP server for programmatic/CI access.

func New

func New(cfg Config, factory SessionFactory) *Server

New creates a new daemon server. If factory is nil, chat endpoint returns an error.

func (*Server) Addr

func (s *Server) Addr() string

Addr returns the listening address.

func (*Server) RegisterReviewRoutes

func (s *Server) RegisterReviewRoutes()

RegisterReviewRoutes adds review endpoints to the daemon. Called from routes() if review support is enabled.

func (*Server) SetReadyFn

func (s *Server) SetReadyFn(fn func() (bool, string))

SetReadyFn installs a custom readiness probe. The probe should return (true, "") when all dependencies (session store, provider connectivity) are initialized, or (false, reason) otherwise. Passing nil restores the default probe. This is additive and safe to call concurrently with serving.

func (*Server) Start

func (s *Server) Start() (string, error)

Start begins serving in the background. Returns the listening address.

func (*Server) Stop

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

Stop gracefully shuts down the daemon.

type Session

type Session struct {
	ID        string    `json:"id"`
	CreatedAt time.Time `json:"created_at"`
	LastUsed  time.Time `json:"last_used"`
	Turns     int       `json:"turns"`
	CWD       string    `json:"cwd"`
}

Session tracks an active daemon session.

type SessionDetailResponse

type SessionDetailResponse struct {
	ID           string    `json:"id"`
	CreatedAt    time.Time `json:"created_at"`
	UpdatedAt    time.Time `json:"updated_at"`
	Model        string    `json:"model"`
	Provider     string    `json:"provider"`
	CWD          string    `json:"cwd"`
	Name         string    `json:"name"`
	MessageCount int       `json:"message_count"`
	ToolCalls    int       `json:"tool_calls"`
}

SessionDetailResponse is the response for GET /v1/sessions/{id}.

type SessionFactory

type SessionFactory func(req ChatRequest) (*engine.Session, error)

SessionFactory creates a configured engine session for a given request. The caller (cmd package) provides this, wiring system prompts, tools, keys.

type SlackConfig

type SlackConfig struct {
	Enabled       bool   `json:"enabled,omitempty"`
	SigningSecret string `json:"signing_secret,omitempty"`
	BotToken      string `json:"bot_token,omitempty"` // xoxb- token for chat.postMessage
	// Path is the HTTP route registered on the daemon mux for the Events API
	// webhook. Defaults to "/v1/slack/events" when empty.
	Path        string   `json:"path,omitempty"`
	PairingCode string   `json:"pairing_code,omitempty"`
	AllowList   []string `json:"allow_list,omitempty"`
}

SlackConfig configures the Slack Events API gateway.

type SlackGateway

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

SlackGateway implements the Slack Events API bridge. Unlike Telegram/Discord it is webhook-driven: Start registers an HTTP handler on the daemon mux and returns after blocking on ctx (so the gatewayManager lifecycle is uniform). Inbound app_mention events are verified against the signing secret, forwarded to the daemon, and answered as threaded replies.

func (*SlackGateway) Name

func (g *SlackGateway) Name() string

Name implements Gateway.

func (*SlackGateway) Start

func (g *SlackGateway) Start(ctx context.Context) error

Start implements Gateway. The webhook is already registered at construction; we simply block until the daemon shuts the gateway down.

func (*SlackGateway) Stop

func (g *SlackGateway) Stop() error

Stop implements Gateway.

type StatsResponse

type StatsResponse struct {
	TotalSessions  int             `json:"total_sessions"`
	TotalMessages  int             `json:"total_messages"`
	TotalToolCalls int             `json:"total_tool_calls"`
	TotalCostUSD   float64         `json:"total_cost_usd"`
	ActiveDays     int             `json:"active_days"`
	Models         []ModelStatResp `json:"models"`
}

StatsResponse is the response for GET /v1/stats.

type TelegramConfig

type TelegramConfig struct {
	Enabled bool   `json:"enabled,omitempty"`
	Token   string `json:"token,omitempty"`
	// PairingCode, when non-empty, requires senders to issue "/pair <code>"
	// once before their sender ID is added to the allowlist.
	PairingCode string `json:"pairing_code,omitempty"`
	// AllowList is an optional set of pre-authorized sender IDs (Telegram usernames
	// or numeric chat IDs as strings). When both PairingCode and AllowList are
	// empty, the gateway refuses all senders (fail closed).
	AllowList []string `json:"allow_list,omitempty"`
}

TelegramConfig configures the Telegram long-poll gateway.

type TelegramGateway

type TelegramGateway struct {
	Token      string
	DaemonAddr string // hawk daemon address to forward messages to
	// contains filtered or unexported fields
}

TelegramGateway connects hawk to a Telegram bot.

func NewTelegramGateway

func NewTelegramGateway(token, daemonAddr string) *TelegramGateway

NewTelegramGateway creates a gateway with the given bot token.

func (*TelegramGateway) Name

func (tg *TelegramGateway) Name() string

Name implements Gateway.

func (*TelegramGateway) Run

func (tg *TelegramGateway) Run(ctx context.Context) error

Run starts the long-polling loop. Blocks until context is cancelled.

func (*TelegramGateway) Start

func (tg *TelegramGateway) Start(ctx context.Context) error

Start implements Gateway by delegating to the long-poll Run loop.

func (*TelegramGateway) Stop

func (tg *TelegramGateway) Stop() error

Stop implements Gateway. The long-poll loop is driven by the context passed to Start, so there is nothing to release here.

type TelegramMessage

type TelegramMessage struct {
	MessageID int    `json:"message_id"`
	Text      string `json:"text"`
	Chat      struct {
		ID int64 `json:"id"`
	} `json:"chat"`
	From struct {
		Username string `json:"username"`
	} `json:"from"`
}

TelegramMessage is a Telegram chat message.

type TelegramUpdate

type TelegramUpdate struct {
	UpdateID int              `json:"update_id"`
	Message  *TelegramMessage `json:"message,omitempty"`
}

TelegramUpdate represents an incoming Telegram message.

Jump to

Keyboard shortcuts

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