agent

package
v0.1.1 Latest Latest
Warning

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

Go to latest
Published: Apr 10, 2026 License: MIT Imports: 26 Imported by: 0

Documentation

Overview

Package agent provides agent lifecycle management for bc.

An agent is an AI assistant running in an isolated tmux session with its own git worktree. Agents have roles (engineer, manager, etc.) that determine their capabilities and permissions.

Basic Usage

Create an agent manager:

mgr := agent.NewWorkspaceManager(".bc/agents", "/path/to/workspace")
if err := mgr.LoadState(); err != nil {
    log.Warn("failed to load state", "error", err)
}

List agents:

for _, a := range mgr.ListAgents() {
    fmt.Printf("%s: %s (%s)\n", a.Name, a.Role, a.State)
}

Start an agent:

ag, err := mgr.Start(ctx, "eng-01", agent.Role("engineer"))
if err != nil {
    log.Fatal(err)
}

Roles and Capabilities

Agents have roles that define their capabilities:

if agent.HasCapability(agent.Role("engineer"), agent.CapImplementTasks) {
    // Engineer can implement tasks
}

Check if a role can create another:

if agent.CanCreateRole(agent.Role("manager"), agent.Role("engineer")) {
    // Manager can spawn engineers
}

States

Agents transition through states: Idle -> Working -> Done/Error. State transitions are validated:

if err := agent.ValidateTransition(agent.StateIdle, agent.StateWorking); err != nil {
    log.Error("invalid transition", "error", err)
}

Index

Constants

View Source
const (
	// DefaultSessionPrefix is the tmux session name prefix for bc agents.
	DefaultSessionPrefix = "bc-"

	// DefaultProvider is the default AI provider for new agents.
	DefaultProvider = "claude"

	// DefaultMaxLogBytes is the maximum log file size in bytes before lazy truncation.
	DefaultMaxLogBytes = 10 * 1024 * 1024 // 10MB
)

Default configuration constants.

View Source
const DefaultBootstrapDelay = 3 * time.Second

DefaultBootstrapDelay is the default time to wait before sending bootstrap prompts after starting an agent. Different AI tools have different startup times, so this can be configured per-manager.

View Source
const DefaultToolHealthInterval = 30 * time.Second

DefaultToolHealthInterval is the default interval between tool health checks.

View Source
const MaxAgentNameLength = 64

MaxAgentNameLength is the maximum allowed length for an agent name.

Variables

AllPermissions lists all available permissions.

RoleCapabilities and RoleHierarchy are empty here. All role definitions (capabilities, hierarchy, metadata) are loaded from workspace .bc/roles/*.md files via RoleManager. Only the root role has hardcoded capabilities.

View Source
var RoleHierarchy = map[Role][]Role{

	RoleRoot: {},
}

Functions

func CanCreateRole

func CanCreateRole(parent, child Role) bool

CanCreateRole checks if a parent role can create a child role.

func CheckPermission

func CheckPermission(permissions []string, required Permission) error

CheckPermission verifies an agent has the required permission. Returns nil if permitted, error otherwise.

func GetAgentCommand

func GetAgentCommand(toolName string) (string, bool)

GetAgentCommand returns the command for a tool name from the provider registry. Returns the command and true if found, or empty string and false if not.

func GetAgentCommandFromConfig

func GetAgentCommandFromConfig(toolName string, wsCfg *workspace.Config) (string, bool)

GetAgentCommandFromConfig returns the command for a tool name, checking workspace ProvidersConfig first, then falling back to global config. This enables per-workspace tool customization.

func HasCapability

func HasCapability(role Role, cap Capability) bool

HasCapability checks if a role has a specific capability.

func HasPermissionStr

func HasPermissionStr(permissions []string, required string) bool

HasPermissionStr checks if a permission string is in the list.

func IsKnownEvent

func IsKnownEvent(ev HookEvent) bool

IsKnownEvent returns true if the event type is recognized (even if informational).

func IsValidAgentName

func IsValidAgentName(name string) bool

IsValidAgentName validates that agent names contain only alphanumeric characters, hyphens, and underscores, and are at most MaxAgentNameLength characters long. This ensures agent names are safe for use in file paths, shell environments, and tmux sessions.

func IsValidState

func IsValidState(s string) bool

IsValidState reports whether s is a known agent state.

func ListAvailableTools

func ListAvailableTools() []string

ListAvailableTools returns a list of configured tool names from the provider registry.

func RoleLevel

func RoleLevel(role Role) int

RoleLevel returns the hierarchy level for built-in roles. Custom roles loaded from .bc/roles/*.md return level 1 by default.

func SetupAgentFromRole

func SetupAgentFromRole(workspacePath, agentName, roleName, targetDir string) error

SetupAgentFromRole sets up agent workspace files for the given role. Defaults to tmux runtime (all MCP transports available).

func SetupAgentFromRoleWithRuntime

func SetupAgentFromRoleWithRuntime(workspacePath, agentName, roleName, targetDir, runtimeBackend string, toolName ...string) error

SetupAgentFromRole resolves a role via BFS inheritance and writes all Claude Code configuration files to the agent's working directory:

  • CLAUDE.md ← role prompt body
  • .mcp.json ← resolved MCP servers with secret injection
  • .claude/settings.json ← role settings (hooks, permissions)
  • .claude/commands/*.md ← custom slash commands
  • .claude/skills/*.md ← reusable skills
  • .claude/agents/*.md ← subagent definitions
  • .claude/rules/*.md ← topic-specific rules
  • REVIEW.md ← code review checklist

SetupAgentFromRoleWithRuntime sets up agent workspace files for the given role, runtime backend, and tool provider. Uses ConfigAdapter for provider-specific file layout (prompt file, config dir, MCP setup, plugins).

func ValidateTransition

func ValidateTransition(from, to State) error

ValidateTransition checks whether a state transition from → to is allowed. Returns an error if the transition is invalid.

func WriteWorkspaceHookSettings

func WriteWorkspaceHookSettings(workspaceRoot string) error

WriteWorkspaceHookSettings writes .claude/settings.json with HTTP-based hooks that POST to bcd's /api/agents/{name}/hook endpoint for instant status updates.

Uses curl to POST JSON payloads. Tool-aware hooks read stdin JSON via jq. This is idempotent: if settings.json already exists the hooks section is merged.

Types

type Agent

type Agent struct {
	UpdatedAt      time.Time    `json:"updated_at"`
	StartedAt      time.Time    `json:"started_at"`
	CreatedAt      time.Time    `json:"created_at"`
	StoppedAt      *time.Time   `json:"stopped_at,omitempty"`
	DeletedAt      *time.Time   `json:"deleted_at,omitempty"`
	RolePrompt     *AgentMemory `json:"memory,omitempty"`
	Workspace      string       `json:"workspace"`
	ID             string       `json:"id"`
	Name           string       `json:"name"`
	Task           string       `json:"task,omitempty"`
	Session        string       `json:"session"`
	SessionID      string       `json:"session_id,omitempty"` // For session resume (#1939)
	Tool           string       `json:"tool,omitempty"`
	ParentID       string       `json:"parent_id,omitempty"`
	HookedWork     string       `json:"hooked_work,omitempty"`
	WorktreeDir    string       `json:"worktree_dir,omitempty"`
	LogFile        string       `json:"log_file,omitempty"`
	Team           string       `json:"team,omitempty"`
	RecoveredFrom  string       `json:"recovered_from,omitempty"`
	EnvFile        string       `json:"env_file,omitempty"`
	RuntimeBackend string       `json:"runtime_backend,omitempty"`
	LastCrashTime  *time.Time   `json:"last_crash_time,omitempty"`
	Role           Role         `json:"role"`
	State          State        `json:"state"`
	Children       []string     `json:"children,omitempty"`
	CrashCount     int          `json:"crash_count,omitempty"`
	IsRoot         bool         `json:"is_root,omitempty"`
}

Agent represents a running AI agent.

func (*Agent) CanCreate

func (a *Agent) CanCreate(childRole Role) bool

CanCreate checks if this agent can create an agent with the given role.

func (*Agent) HasCapability

func (a *Agent) HasCapability(cap Capability) bool

HasCapability checks if this agent has a specific capability.

func (*Agent) IsLeaf

func (a *Agent) IsLeaf() bool

IsLeaf returns true if this agent has no children.

func (*Agent) Level

func (a *Agent) Level() int

Level returns the hierarchy level of this agent.

type AgentMemory

type AgentMemory struct {
	// LoadedAt is when the memory was loaded.
	LoadedAt time.Time `json:"loaded_at,omitempty"`
	// RolePrompt is the full content of the role's prompt file.
	RolePrompt string `json:"role_prompt,omitempty"`
	// Plugins lists Claude Code plugin names to install on agent start (#1959).
	Plugins []string `json:"plugins,omitempty"`
}

AgentMemory holds role-specific content loaded from prompts/<role>.md.

func LoadRoleMemory

func LoadRoleMemory(workspacePath string, role Role) *AgentMemory

LoadRoleMemory loads role-specific prompt content from .bc/roles/<role>.md. For the root role, loads from .bc/prompts/root.md for backward compatibility. Returns nil AgentMemory if the file doesn't exist.

type AgentService

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

AgentService provides the application-level API for agent management. It wraps Manager with event publishing and cost queries. This is the boundary that the daemon (issue #1938) will use.

func NewAgentService

func NewAgentService(mgr *Manager, events EventPublisher, costs CostQuerier) *AgentService

NewAgentService creates a new agent service wrapping the given manager. It registers a state-change callback on the manager so that ongoing state transitions (hook events) are published as SSE events.

func (*AgentService) Broadcast

func (s *AgentService) Broadcast(ctx context.Context, message string) (int, error)

Broadcast sends a message to all running agents. Returns the number of agents the message was sent to.

func (*AgentService) Cost

func (s *AgentService) Cost(ctx context.Context, name string) (*CostSummary, error)

Cost returns the cost summary for an agent.

func (*AgentService) Create

func (s *AgentService) Create(ctx context.Context, opts CreateOptions) (*Agent, error)

Create creates a new agent.

func (*AgentService) Delete

func (s *AgentService) Delete(ctx context.Context, name string, force bool) error

Delete permanently removes an agent. Agent must be stopped first unless force is true.

func (*AgentService) GenerateName

func (s *AgentService) GenerateName(ctx context.Context) (string, error)

GenerateName generates a unique agent name not already in use.

func (*AgentService) Get

func (s *AgentService) Get(ctx context.Context, name string) (*Agent, error)

Get returns a single agent by name.

func (*AgentService) List

func (s *AgentService) List(ctx context.Context, opts ListOptions) ([]*Agent, error)

List returns agents matching the given options.

func (*AgentService) Manager

func (s *AgentService) Manager() *Manager

Manager returns the underlying agent manager.

func (*AgentService) Peek

func (s *AgentService) Peek(ctx context.Context, name string, lines int) (string, error)

Peek returns recent output from an agent.

func (*AgentService) Rename

func (s *AgentService) Rename(ctx context.Context, oldName, newName string) error

Rename renames an agent.

func (*AgentService) Send

func (s *AgentService) Send(ctx context.Context, name, message string) error

Send sends a message to a running agent.

func (*AgentService) SendToPattern

func (s *AgentService) SendToPattern(ctx context.Context, pattern, message string) (SendResult, error)

SendToPattern sends a message to all agents whose names match the given glob pattern.

func (*AgentService) SendToRole

func (s *AgentService) SendToRole(ctx context.Context, role, message string) (SendResult, error)

SendToRole sends a message to all running agents with the given role.

func (*AgentService) Sessions

func (s *AgentService) Sessions(ctx context.Context, name string) ([]SessionEntry, error)

Sessions returns session history for an agent.

func (*AgentService) Start

func (s *AgentService) Start(ctx context.Context, name string, opts StartOptions) (*Agent, error)

Start starts a stopped agent, optionally with a fresh session.

func (*AgentService) Stop

func (s *AgentService) Stop(ctx context.Context, name string) error

Stop stops a running agent.

func (*AgentService) StopAll

func (s *AgentService) StopAll(ctx context.Context) (int, error)

StopAll stops all running agents. Returns count of agents stopped.

type AgentState

type AgentState struct {
	StartedAt time.Time `json:"started_at"`
	UpdatedAt time.Time `json:"updated_at"`
	Name      string    `json:"name"`
	Tool      string    `json:"tool,omitempty"`
	Team      string    `json:"team,omitempty"`
	Parent    string    `json:"parent,omitempty"`
	Worktree  string    `json:"worktree,omitempty"`
	Session   string    `json:"session,omitempty"`
	Role      Role      `json:"role"`
	State     State     `json:"state"`
}

AgentState is the legacy per-agent JSON state format (v1). Only used during migration from JSON files to SQLite.

func (*AgentState) ToAgent

func (s *AgentState) ToAgent(workspace string) *Agent

ToAgent converts a legacy AgentState to the current Agent struct.

type AgentStatsRecord

type AgentStatsRecord struct {
	CollectedAt  time.Time `json:"collected_at"`
	AgentName    string    `json:"agent_name"`
	CPUPct       float64   `json:"cpu_pct"`
	MemUsedMB    float64   `json:"mem_used_mb"`
	MemLimitMB   float64   `json:"mem_limit_mb"`
	NetRxMB      float64   `json:"net_rx_mb"`
	NetTxMB      float64   `json:"net_tx_mb"`
	BlockReadMB  float64   `json:"block_read_mb"`
	BlockWriteMB float64   `json:"block_write_mb"`
}

AgentStatsRecord holds a single stats sample for an agent.

type Capability

type Capability string

Capability defines what actions a role can perform.

const (
	CapCreateAgents   Capability = "create_agents"   // Can spawn child agents
	CapAssignWork     Capability = "assign_work"     // Can assign work to others
	CapCreateEpics    Capability = "create_epics"    // Can create high-level epics
	CapImplementTasks Capability = "implement_tasks" // Can write code/implement
	CapReviewWork     Capability = "review_work"     // Can review others' work
	CapTestWork       Capability = "test_work"       // Can test and validate implementations
)

type CostQuerier

type CostQuerier interface {
	AgentCostSummary(agentID string) (*CostSummary, error)
}

CostQuerier is the interface for querying agent cost data.

type CostSummary

type CostSummary struct {
	AgentID      string  `json:"agent_id"`
	InputTokens  int64   `json:"input_tokens"`
	OutputTokens int64   `json:"output_tokens"`
	TotalTokens  int64   `json:"total_tokens"`
	TotalCostUSD float64 `json:"total_cost_usd"`
	RequestCount int64   `json:"request_count"`
}

CostSummary holds cost breakdown for an agent.

type CreateOptions

type CreateOptions struct {
	Name    string
	Role    Role
	Tool    string
	EnvFile string
	Runtime string
	Parent  string
	Team    string
}

CreateOptions holds parameters for creating an agent via the service.

type DeleteOptions

type DeleteOptions struct {
	// Placeholder for future options.
	Force bool
}

DeleteOptions configures agent deletion behavior.

type EventPublisher

type EventPublisher interface {
	Publish(eventType string, data map[string]any)
}

EventPublisher is the interface for publishing agent lifecycle events.

type HookEvent

type HookEvent string

HookEvent is a lifecycle event type — either a Claude Code hook or a bc-internal event.

const (
	HookSessionStart       HookEvent = "SessionStart"
	HookSessionEnd         HookEvent = "SessionEnd"
	HookUserPromptSubmit   HookEvent = "UserPromptSubmit"
	HookPreToolUse         HookEvent = "PreToolUse"
	HookPostToolUse        HookEvent = "PostToolUse"
	HookPostToolUseFailure HookEvent = "PostToolUseFailure"
	HookPermissionRequest  HookEvent = "PermissionRequest"
	HookStop               HookEvent = "Stop"
	HookNotification       HookEvent = "Notification"
	HookSubagentStart      HookEvent = "SubagentStart"
	HookSubagentStop       HookEvent = "SubagentStop"
	HookTaskCompleted      HookEvent = "TaskCompleted"
	HookTeammateIdle       HookEvent = "TeammateIdle"
	HookInstructionsLoaded HookEvent = "InstructionsLoaded"
	HookConfigChange       HookEvent = "ConfigChange"
	HookWorktreeCreate     HookEvent = "WorktreeCreate"
	HookWorktreeRemove     HookEvent = "WorktreeRemove"
	HookPreCompact         HookEvent = "PreCompact"
	HookPostCompact        HookEvent = "PostCompact"
	HookElicitation        HookEvent = "Elicitation"
	HookElicitationResult  HookEvent = "ElicitationResult"
)
const (
	HookChannelMessage HookEvent = "ChannelMessage"
	HookChannelSent    HookEvent = "ChannelSent"
	HookAgentMessage   HookEvent = "AgentMessage"
	HookCostUpdate     HookEvent = "CostUpdate"
)

type HookPayload

type HookPayload struct {
	ToolInput    any       `json:"tool_input,omitempty"`
	SubagentID   string    `json:"subagent_id,omitempty"`
	Channel      string    `json:"channel,omitempty"`
	State        string    `json:"state,omitempty"`
	Task         string    `json:"task,omitempty"`
	ToolName     string    `json:"tool_name,omitempty"`
	Command      string    `json:"command,omitempty"`
	Error        string    `json:"error,omitempty"`
	Model        string    `json:"model,omitempty"`
	Event        HookEvent `json:"event"`
	Sender       string    `json:"sender,omitempty"`
	SubagentType string    `json:"subagent_type,omitempty"`
	Message      string    `json:"message,omitempty"`
	File         string    `json:"file,omitempty"`
	Mentions     []string  `json:"mentions,omitempty"`
	CostUSD      float64   `json:"cost_usd,omitempty"`
	InputTokens  int64     `json:"input_tokens,omitempty"`
	OutputTokens int64     `json:"output_tokens,omitempty"`
}

HookPayload is the JSON payload received by the /hook endpoint. Different events populate different fields.

type ListOptions

type ListOptions struct {
	Role   string // Filter by role (empty = all)
	Status string // Filter by status/state (empty = all)
}

ListOptions configures agent listing.

type Manager

type Manager struct {

	// BootstrapDelay is the time to wait before sending bootstrap prompts.
	// If zero, DefaultBootstrapDelay is used.
	BootstrapDelay time.Duration
	// contains filtered or unexported fields
}

Manager handles agent lifecycle.

func NewManager

func NewManager(stateDir string) *Manager

NewManager creates a new agent manager with workspace-scoped tmux sessions.

func NewWorkspaceManager

func NewWorkspaceManager(stateDir, workspacePath string) *Manager

NewWorkspaceManager creates an agent manager scoped to a workspace. Session names will be unique per workspace to avoid collisions.

func NewWorkspaceManagerWithRuntime

func NewWorkspaceManagerWithRuntime(stateDir, workspacePath string, rt runtime.Backend, rtName string) *Manager

NewWorkspaceManagerWithRuntime creates an agent manager with a specific runtime backend. rtName should be "docker" or "tmux".

func (*Manager) AgentCount

func (m *Manager) AgentCount() int

AgentCount returns the number of agents.

func (*Manager) ApplyWorkspaceConfig

func (m *Manager) ApplyWorkspaceConfig(cfg *workspace.Config)

ApplyWorkspaceConfig applies workspace-level configuration overrides to the manager. This should be called after creating a manager to pick up workspace-specific settings.

func (*Manager) AttachToAgent

func (m *Manager) AttachToAgent(ctx context.Context, name string) error

AttachToAgent returns the command to attach to an agent's session.

func (*Manager) CaptureOutput

func (m *Manager) CaptureOutput(ctx context.Context, name string, lines int) (string, error)

CaptureOutput captures recent output from an agent's session. Reads from the agent's log file first (includes full history with ANSI). Falls back to tmux capture-pane if log file is not available.

func (*Manager) CheckToolHealth

func (m *Manager) CheckToolHealth(ctx context.Context, agentName string) error

CheckToolHealth verifies that the tool binary for the named agent is still installed. If the binary is missing, the agent is marked as StateStuck and diagnostic information is logged. This is intended to be called from a health-check loop or on-demand.

func (*Manager) Close

func (m *Manager) Close() error

Close closes the SQLite store. Call when done with the manager.

func (*Manager) DeleteAgent

func (m *Manager) DeleteAgent(ctx context.Context, name string) error

DeleteAgent permanently removes an agent from the workspace.

func (*Manager) DeleteAgentWithOptions

func (m *Manager) DeleteAgentWithOptions(ctx context.Context, name string, opts DeleteOptions) error

DeleteAgentWithOptions permanently removes an agent with configurable options. Cleans up all resources: container, volume, worktree, git branch, log file, agent state directory, channel memberships, and child agent references. Partial failures are logged but do not abort the deletion.

func (*Manager) FollowOutput

func (m *Manager) FollowOutput(ctx context.Context, name string, lines int, w io.Writer) error

FollowOutput streams new log lines in real-time, like tail -f. It prints the last N lines first, then polls for new content every 200ms. Blocks until the context is canceled. Falls back to a one-shot CaptureOutput if no log file exists.

func (*Manager) GetAgent

func (m *Manager) GetAgent(name string) *Agent

GetAgent returns a copy of an agent by name. Returns nil if the agent doesn't exist.

func (*Manager) GetParent

func (m *Manager) GetParent(agentID string) *Agent

GetParent returns a copy of the parent agent, or nil if no parent.

func (*Manager) ListAgents

func (m *Manager) ListAgents() []*Agent

ListAgents returns copies of all agents sorted by role hierarchy then by name. Order: ProductManager/Coordinator → Manager → Engineer/Worker

func (*Manager) ListByRole

func (m *Manager) ListByRole(role Role) []*Agent

ListByRole returns copies of all agents with a specific role.

func (*Manager) ListChildren

func (m *Manager) ListChildren(parentID string) []*Agent

ListChildren returns copies of all direct children of an agent.

func (*Manager) ListDescendants

func (m *Manager) ListDescendants(parentID string) []*Agent

ListDescendants returns all descendants of an agent (children, grandchildren, etc.).

func (*Manager) LoadState

func (m *Manager) LoadState() error

LoadState loads agent state from SQLite. On first run after upgrade, migrates JSON files to SQLite automatically.

func (*Manager) QueryAgentStats

func (m *Manager) QueryAgentStats(agentName string, limit int) ([]*AgentStatsRecord, error)

QueryAgentStats returns up to limit recent stats records for the named agent.

func (*Manager) RecordAgentStats

func (m *Manager) RecordAgentStats(rec *AgentStatsRecord) error

RecordAgentStats persists a single AgentStatsRecord to the SQLite store. This is used by the background container metrics collector to save Docker resource samples so the /api/agents/{name}/stats endpoint returns real data.

func (*Manager) RenameAgent

func (m *Manager) RenameAgent(ctx context.Context, oldName, newName string) error

RenameAgent renames an agent from oldName to newName.

func (*Manager) RunningCount

func (m *Manager) RunningCount() int

RunningCount returns the number of non-stopped agents.

func (*Manager) Runtime

func (m *Manager) Runtime() runtime.Backend

Runtime returns the default runtime backend for session management.

func (*Manager) RuntimeForAgent

func (m *Manager) RuntimeForAgent(name string) runtime.Backend

RuntimeForAgent returns the runtime backend for a specific agent.

func (*Manager) SendToAgent

func (m *Manager) SendToAgent(ctx context.Context, name, message string) error

SendToAgent sends a message/command to an agent's session. Sends Enter after the message to submit it.

func (*Manager) SetAgentByName

func (m *Manager) SetAgentByName(name string) bool

SetAgentByName sets the agent command by looking up the provider name in the registry.

func (*Manager) SetAgentCommand

func (m *Manager) SetAgentCommand(cmd string)

SetAgentCommand sets the command to run for agents.

func (*Manager) SetAgentTeam

func (m *Manager) SetAgentTeam(name, team string) error

SetAgentTeam assigns an agent to a team.

func (*Manager) SetBootstrapDelay

func (m *Manager) SetBootstrapDelay(d time.Duration)

SetBootstrapDelay sets the delay before sending bootstrap prompts.

func (*Manager) SetOnStateChange

func (m *Manager) SetOnStateChange(fn func(name string, state State, task string))

SetOnStateChange registers a callback invoked whenever an agent's state changes through hook-event processing.

func (*Manager) SetRoleManager

func (m *Manager) SetRoleManager(rm *workspace.RoleManager)

SetRoleManager sets the role manager used for role validation.

func (*Manager) SpawnAgent

func (m *Manager) SpawnAgent(ctx context.Context, name string, role Role, workspace string) (*Agent, error)

SpawnAgent creates and starts a new agent. Idempotent: if the agent already exists and its tmux session is alive, reuse it.

func (*Manager) SpawnAgentWithOptions

func (m *Manager) SpawnAgentWithOptions(ctx context.Context, opts SpawnOptions) (*Agent, error)

SpawnAgentWithOptions creates and starts a new agent with all options. If tool is empty, uses the manager's default agent command. Idempotent: if the agent already exists and its tmux session is alive, reuse it.

func (*Manager) SpawnAgentWithParent

func (m *Manager) SpawnAgentWithParent(ctx context.Context, name string, role Role, workspace string, parentID string) (*Agent, error)

SpawnAgentWithParent creates and starts a new agent with a parent relationship. Idempotent: if the agent already exists and its tmux session is alive, reuse it.

func (*Manager) SpawnAgentWithTool

func (m *Manager) SpawnAgentWithTool(ctx context.Context, name string, role Role, workspace string, tool string) (*Agent, error)

SpawnAgentWithTool creates and starts a new agent with a specific tool. If tool is empty, uses the manager's default agent command.

func (*Manager) SpawnChildAgent

func (m *Manager) SpawnChildAgent(ctx context.Context, parentID, childName string, childRole Role, workspace string) (*Agent, error)

SpawnChildAgent creates a child agent under a parent agent. Validates that the parent has permission to create the child role.

func (*Manager) SpawnChildAgentWithTool

func (m *Manager) SpawnChildAgentWithTool(ctx context.Context, parentID, childName string, childRole Role, workspace, tool string) (*Agent, error)

SpawnChildAgentWithTool creates a child agent under a parent agent with a specific tool. Validates that the parent has permission to create the child role.

func (*Manager) StartToolHealthLoop

func (m *Manager) StartToolHealthLoop(ctx context.Context, interval time.Duration)

StartToolHealthLoop launches a background goroutine that periodically checks tool availability for all running agents. It calls CheckToolHealth for each agent on every tick, logging state transitions (tool became unavailable or recovered). The loop respects context cancellation for clean shutdown.

func (*Manager) StopAgent

func (m *Manager) StopAgent(ctx context.Context, name string) error

StopAgent stops an agent.

func (*Manager) StopAgentTree

func (m *Manager) StopAgentTree(ctx context.Context, name string) error

StopAgentTree stops an agent and all its children recursively.

func (*Manager) StopAll

func (m *Manager) StopAll(ctx context.Context) error

StopAll stops all agents.

func (*Manager) StopToolHealthLoop

func (m *Manager) StopToolHealthLoop()

StopToolHealthLoop cancels the background tool health check goroutine.

func (*Manager) Tmux

func (m *Manager) Tmux() *tmux.Manager

Tmux returns the underlying tmux manager if the default backend is tmux. Deprecated: Use Runtime() instead. This is kept for backward compatibility.

func (*Manager) UpdateAgentState

func (m *Manager) UpdateAgentState(name string, state State, task string) error

UpdateAgentState updates an agent's state and task. Returns an error if the transition is invalid per the state machine.

type Permission

type Permission string

Permission defines RBAC permissions for agent operations. Issue #1191: RBAC permissions for agent capabilities

const (
	// Agent lifecycle permissions
	PermCreateAgents  Permission = "can_create_agents"  // Can spawn new agents
	PermStopAgents    Permission = "can_stop_agents"    // Can stop running agents
	PermDeleteAgents  Permission = "can_delete_agents"  // Can permanently delete agents
	PermRestartAgents Permission = "can_restart_agents" // Can restart stopped agents

	// Communication permissions
	PermSendCommands Permission = "can_send_commands" // Can send commands to agents
	PermViewLogs     Permission = "can_view_logs"     // Can view agent logs/output

	// Configuration permissions
	PermModifyConfig Permission = "can_modify_config" // Can modify workspace config
	PermModifyRoles  Permission = "can_modify_roles"  // Can edit role definitions

	// Channel permissions
	PermCreateChannels Permission = "can_create_channels" // Can create new channels
	PermDeleteChannels Permission = "can_delete_channels" // Can delete channels
	PermSendMessages   Permission = "can_send_messages"   // Can send messages to channels
)

func DefaultPermissions

func DefaultPermissions(roleLevel int) []Permission

DefaultPermissions returns default permissions for a role level. Higher level roles (root, manager) have more permissions by default.

type Role

type Role string

Role defines the type of agent.

const (
	// RoleRoot is the only hardcoded role - a singleton root agent.
	// All other roles are defined in workspace .bc/roles/*.md files.
	RoleRoot Role = "root"
)

type SQLiteStore

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

SQLiteStore provides SQLite-backed persistence for agent state. It replaces the JSON file-based storage (agents.json, root.json, per-agent JSONs) with a single bc.db using WAL mode for safe concurrent access.

func NewSQLiteStore

func NewSQLiteStore(dbPath string) (*SQLiteStore, error)

NewSQLiteStore opens (or creates) the state database at dbPath and ensures the agents table exists.

func (*SQLiteStore) Close

func (s *SQLiteStore) Close() error

Close closes the database.

func (*SQLiteStore) Delete

func (s *SQLiteStore) Delete(name string) error

Delete removes a single agent by name.

func (*SQLiteStore) Load

func (s *SQLiteStore) Load(name string) (*Agent, error)

Load reads a single agent by name. Returns nil, nil if not found.

func (*SQLiteStore) LoadAll

func (s *SQLiteStore) LoadAll() (map[string]*Agent, error)

LoadAll reads every non-deleted agent into a map keyed by name. Agents with a non-null deleted_at are skipped to prevent resurrection after restart.

func (*SQLiteStore) LoadRoot

func (s *SQLiteStore) LoadRoot() (*Agent, error)

LoadRoot reads the root agent (is_root=1). Returns nil, nil if not found.

func (*SQLiteStore) QueryStats

func (s *SQLiteStore) QueryStats(agentName string, limit int) ([]*AgentStatsRecord, error)

QueryStats returns the most recent limit stats rows for an agent, newest first.

func (*SQLiteStore) Save

func (s *SQLiteStore) Save(a *Agent) error

Save persists a single agent (INSERT OR REPLACE).

func (*SQLiteStore) SaveAll

func (s *SQLiteStore) SaveAll(agents map[string]*Agent) error

SaveAll persists every agent in the map inside a single transaction.

func (*SQLiteStore) SaveStats

func (s *SQLiteStore) SaveStats(rec *AgentStatsRecord) error

SaveStats inserts a single AgentStatsRecord into the agent_stats table.

func (*SQLiteStore) SoftDelete

func (s *SQLiteStore) SoftDelete(name string) error

SoftDelete marks an agent as deleted by setting deleted_at. The agent row remains in the database but is excluded from LoadAll.

func (*SQLiteStore) UpdateField

func (s *SQLiteStore) UpdateField(name, field, value string) error

UpdateField updates a single text column for a given agent.

func (*SQLiteStore) UpdateState

func (s *SQLiteStore) UpdateState(name string, state State) error

UpdateState updates only the state column for a given agent.

type SendResult

type SendResult struct {
	Matched []string `json:"matched"`
	Sent    int      `json:"sent"`
	Skipped int      `json:"skipped"`
	Failed  int      `json:"failed"`
}

SendResult holds the result of a broadcast/role/pattern send operation.

type SessionEntry

type SessionEntry struct {
	Timestamp time.Time `json:"timestamp,omitempty"`
	ID        string    `json:"id"`
	Current   bool      `json:"current,omitempty"`
}

SessionEntry represents a single session history record.

type SpawnOptions

type SpawnOptions struct {
	Name      string
	Role      Role
	Workspace string
	ParentID  string
	Tool      string
	EnvFile   string
	Runtime   string // override runtime backend ("tmux" or "docker"); empty uses manager default
	Team      string // optional team assignment
	SessionID string // Explicit session ID to resume (overrides stored session_id)
}

SpawnOptions holds all parameters for creating an agent.

type StartOptions

type StartOptions struct {
	Runtime  string // Runtime backend override
	ResumeID string // Explicit session ID to resume
}

StartOptions configures agent start behavior.

type State

type State string

State represents the current state of an agent.

const (
	StateIdle     State = "idle"
	StateStarting State = "starting"
	StateWorking  State = "working"
	StateDone     State = "done"
	StateStuck    State = "stuck"
	StateError    State = "error"
	StateStopped  State = "stopped"
)

func StateForHookEvent

func StateForHookEvent(ev HookEvent) (State, bool)

StateForHookEvent returns the target agent State for a hook event. Returns false if the event doesn't trigger a state change (informational events).

Jump to

Keyboard shortcuts

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