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
- Variables
- func CanCreateRole(parent, child Role) bool
- func CheckPermission(permissions []string, required Permission) error
- func GetAgentCommand(toolName string) (string, bool)
- func GetAgentCommandFromConfig(toolName string, wsCfg *workspace.Config) (string, bool)
- func HasCapability(role Role, cap Capability) bool
- func HasPermissionStr(permissions []string, required string) bool
- func IsKnownEvent(ev HookEvent) bool
- func IsValidAgentName(name string) bool
- func IsValidState(s string) bool
- func ListAvailableTools() []string
- func RoleLevel(role Role) int
- func SetupAgentFromRole(workspacePath, agentName, roleName, targetDir string) error
- func SetupAgentFromRoleWithRuntime(workspacePath, agentName, roleName, targetDir, runtimeBackend string, ...) error
- func ValidateTransition(from, to State) error
- func WriteWorkspaceHookSettings(workspaceRoot string) error
- type Agent
- type AgentMemory
- type AgentService
- func (s *AgentService) Broadcast(ctx context.Context, message string) (int, error)
- func (s *AgentService) Cost(ctx context.Context, name string) (*CostSummary, error)
- func (s *AgentService) Create(ctx context.Context, opts CreateOptions) (*Agent, error)
- func (s *AgentService) Delete(ctx context.Context, name string, force bool) error
- func (s *AgentService) GenerateName(ctx context.Context) (string, error)
- func (s *AgentService) Get(ctx context.Context, name string) (*Agent, error)
- func (s *AgentService) List(ctx context.Context, opts ListOptions) ([]*Agent, error)
- func (s *AgentService) Manager() *Manager
- func (s *AgentService) Peek(ctx context.Context, name string, lines int) (string, error)
- func (s *AgentService) Rename(ctx context.Context, oldName, newName string) error
- func (s *AgentService) Send(ctx context.Context, name, message string) error
- func (s *AgentService) SendToPattern(ctx context.Context, pattern, message string) (SendResult, error)
- func (s *AgentService) SendToRole(ctx context.Context, role, message string) (SendResult, error)
- func (s *AgentService) Sessions(ctx context.Context, name string) ([]SessionEntry, error)
- func (s *AgentService) Start(ctx context.Context, name string, opts StartOptions) (*Agent, error)
- func (s *AgentService) Stop(ctx context.Context, name string) error
- func (s *AgentService) StopAll(ctx context.Context) (int, error)
- type AgentState
- type AgentStatsRecord
- type Capability
- type CostQuerier
- type CostSummary
- type CreateOptions
- type DeleteOptions
- type EventPublisher
- type HookEvent
- type HookPayload
- type ListOptions
- type Manager
- func (m *Manager) AgentCount() int
- func (m *Manager) ApplyWorkspaceConfig(cfg *workspace.Config)
- func (m *Manager) AttachToAgent(ctx context.Context, name string) error
- func (m *Manager) CaptureOutput(ctx context.Context, name string, lines int) (string, error)
- func (m *Manager) CheckToolHealth(ctx context.Context, agentName string) error
- func (m *Manager) Close() error
- func (m *Manager) DeleteAgent(ctx context.Context, name string) error
- func (m *Manager) DeleteAgentWithOptions(ctx context.Context, name string, opts DeleteOptions) error
- func (m *Manager) FollowOutput(ctx context.Context, name string, lines int, w io.Writer) error
- func (m *Manager) GetAgent(name string) *Agent
- func (m *Manager) GetParent(agentID string) *Agent
- func (m *Manager) ListAgents() []*Agent
- func (m *Manager) ListByRole(role Role) []*Agent
- func (m *Manager) ListChildren(parentID string) []*Agent
- func (m *Manager) ListDescendants(parentID string) []*Agent
- func (m *Manager) LoadState() error
- func (m *Manager) QueryAgentStats(agentName string, limit int) ([]*AgentStatsRecord, error)
- func (m *Manager) RecordAgentStats(rec *AgentStatsRecord) error
- func (m *Manager) RenameAgent(ctx context.Context, oldName, newName string) error
- func (m *Manager) RunningCount() int
- func (m *Manager) Runtime() runtime.Backend
- func (m *Manager) RuntimeForAgent(name string) runtime.Backend
- func (m *Manager) SendToAgent(ctx context.Context, name, message string) error
- func (m *Manager) SetAgentByName(name string) bool
- func (m *Manager) SetAgentCommand(cmd string)
- func (m *Manager) SetAgentTeam(name, team string) error
- func (m *Manager) SetBootstrapDelay(d time.Duration)
- func (m *Manager) SetOnStateChange(fn func(name string, state State, task string))
- func (m *Manager) SetRoleManager(rm *workspace.RoleManager)
- func (m *Manager) SpawnAgent(ctx context.Context, name string, role Role, workspace string) (*Agent, error)
- func (m *Manager) SpawnAgentWithOptions(ctx context.Context, opts SpawnOptions) (*Agent, error)
- func (m *Manager) SpawnAgentWithParent(ctx context.Context, name string, role Role, workspace string, parentID string) (*Agent, error)
- func (m *Manager) SpawnAgentWithTool(ctx context.Context, name string, role Role, workspace string, tool string) (*Agent, error)
- func (m *Manager) SpawnChildAgent(ctx context.Context, parentID, childName string, childRole Role, ...) (*Agent, error)
- func (m *Manager) SpawnChildAgentWithTool(ctx context.Context, parentID, childName string, childRole Role, ...) (*Agent, error)
- func (m *Manager) StartToolHealthLoop(ctx context.Context, interval time.Duration)
- func (m *Manager) StopAgent(ctx context.Context, name string) error
- func (m *Manager) StopAgentTree(ctx context.Context, name string) error
- func (m *Manager) StopAll(ctx context.Context) error
- func (m *Manager) StopToolHealthLoop()
- func (m *Manager) Tmux() *tmux.Manager
- func (m *Manager) UpdateAgentState(name string, state State, task string) error
- type Permission
- type Role
- type SQLiteStore
- func (s *SQLiteStore) Close() error
- func (s *SQLiteStore) Delete(name string) error
- func (s *SQLiteStore) Load(name string) (*Agent, error)
- func (s *SQLiteStore) LoadAll() (map[string]*Agent, error)
- func (s *SQLiteStore) LoadRoot() (*Agent, error)
- func (s *SQLiteStore) QueryStats(agentName string, limit int) ([]*AgentStatsRecord, error)
- func (s *SQLiteStore) Save(a *Agent) error
- func (s *SQLiteStore) SaveAll(agents map[string]*Agent) error
- func (s *SQLiteStore) SaveStats(rec *AgentStatsRecord) error
- func (s *SQLiteStore) SoftDelete(name string) error
- func (s *SQLiteStore) UpdateField(name, field, value string) error
- func (s *SQLiteStore) UpdateState(name string, state State) error
- type SendResult
- type SessionEntry
- type SpawnOptions
- type StartOptions
- type State
Constants ¶
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.
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.
const DefaultToolHealthInterval = 30 * time.Second
DefaultToolHealthInterval is the default interval between tool health checks.
const MaxAgentNameLength = 64
MaxAgentNameLength is the maximum allowed length for an agent name.
Variables ¶
var AllPermissions = []Permission{ PermCreateAgents, PermStopAgents, PermDeleteAgents, PermRestartAgents, PermSendCommands, PermViewLogs, PermModifyConfig, PermModifyRoles, PermCreateChannels, PermDeleteChannels, PermSendMessages, }
AllPermissions lists all available permissions.
var RoleCapabilities = map[Role][]Capability{ RoleRoot: {CapCreateAgents, CapAssignWork, CapCreateEpics, CapReviewWork}, }
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.
var RoleHierarchy = map[Role][]Role{ RoleRoot: {}, }
Functions ¶
func CanCreateRole ¶
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 ¶
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 ¶
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 ¶
HasPermissionStr checks if a permission string is in the list.
func IsKnownEvent ¶
IsKnownEvent returns true if the event type is recognized (even if informational).
func IsValidAgentName ¶
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 ¶
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 ¶
RoleLevel returns the hierarchy level for built-in roles. Custom roles loaded from .bc/roles/*.md return level 1 by default.
func SetupAgentFromRole ¶
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 ¶
ValidateTransition checks whether a state transition from → to is allowed. Returns an error if the transition is invalid.
func WriteWorkspaceHookSettings ¶
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) HasCapability ¶
func (a *Agent) HasCapability(cap Capability) bool
HasCapability checks if this agent has a specific capability.
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 ¶
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 ¶
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) 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) 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.
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 ¶
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" )
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 ¶
NewManager creates a new agent manager with workspace-scoped tmux sessions.
func NewWorkspaceManager ¶
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 ¶
AgentCount returns the number of agents.
func (*Manager) ApplyWorkspaceConfig ¶
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 ¶
AttachToAgent returns the command to attach to an agent's session.
func (*Manager) CaptureOutput ¶
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 ¶
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) DeleteAgent ¶
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 ¶
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 ¶
GetAgent returns a copy of an agent by name. Returns nil if the agent doesn't exist.
func (*Manager) ListAgents ¶
ListAgents returns copies of all agents sorted by role hierarchy then by name. Order: ProductManager/Coordinator → Manager → Engineer/Worker
func (*Manager) ListByRole ¶
ListByRole returns copies of all agents with a specific role.
func (*Manager) ListChildren ¶
ListChildren returns copies of all direct children of an agent.
func (*Manager) ListDescendants ¶
ListDescendants returns all descendants of an agent (children, grandchildren, etc.).
func (*Manager) LoadState ¶
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 ¶
RenameAgent renames an agent from oldName to newName.
func (*Manager) RunningCount ¶
RunningCount returns the number of non-stopped agents.
func (*Manager) RuntimeForAgent ¶
RuntimeForAgent returns the runtime backend for a specific agent.
func (*Manager) SendToAgent ¶
SendToAgent sends a message/command to an agent's session. Sends Enter after the message to submit it.
func (*Manager) SetAgentByName ¶
SetAgentByName sets the agent command by looking up the provider name in the registry.
func (*Manager) SetAgentCommand ¶
SetAgentCommand sets the command to run for agents.
func (*Manager) SetAgentTeam ¶
SetAgentTeam assigns an agent to a team.
func (*Manager) SetBootstrapDelay ¶
SetBootstrapDelay sets the delay before sending bootstrap prompts.
func (*Manager) SetOnStateChange ¶
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 ¶
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 ¶
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) StopAgentTree ¶
StopAgentTree stops an agent and all its children recursively.
func (*Manager) StopToolHealthLoop ¶
func (m *Manager) StopToolHealthLoop()
StopToolHealthLoop cancels the background tool health check goroutine.
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) 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.
func StateForHookEvent ¶
StateForHookEvent returns the target agent State for a hook event. Returns false if the event doesn't trigger a state change (informational events).