Documentation
¶
Index ¶
- Variables
- func EstimateMessageTokens(msg providers.Message) int
- func EstimateToolDefsTokens(defs []providers.ToolDefinition) int
- func RegisterBuiltinHook(name string, factory BuiltinHookFactory) error
- func RegisterContextManager(name string, factory ContextManagerFactory) error
- func SpawnSubTurn(ctx context.Context, cfg SubTurnConfig) (*tools.ToolResult, error)
- func TurnStateFromContext(ctx context.Context) *turnState
- func WithAgentLoop(ctx context.Context, al *AgentLoop) context.Context
- type ActiveTurnInfo
- type AgentContextDefinition
- type AgentDescriptor
- type AgentFrontmatter
- type AgentInstance
- type AgentLoop
- func (al *AgentLoop) Close()
- func (al *AgentLoop) Continue(ctx context.Context, sessionKey, channel, chatID string) (string, error)
- func (al *AgentLoop) DefaultModelStatus() (string, error)
- func (al *AgentLoop) GetActiveTurnBySession(sessionKey string) *ActiveTurnInfo
- func (al *AgentLoop) GetConfig() *config.Config
- func (al *AgentLoop) GetRegistry() *AgentRegistry
- func (al *AgentLoop) GetStartupInfo() map[string]any
- func (al *AgentLoop) HardAbort(sessionKey string) error
- func (al *AgentLoop) MountHook(reg HookRegistration) error
- func (al *AgentLoop) MountProcessHook(ctx context.Context, name string, opts ProcessHookOptions) error
- func (al *AgentLoop) ProcessDirect(ctx context.Context, content, sessionKey string) (string, error)
- func (al *AgentLoop) ProcessDirectWithChannel(ctx context.Context, content, sessionKey, channel, chatID string) (string, error)
- func (al *AgentLoop) ProcessHeartbeat(ctx context.Context, content, channel, chatID string) (string, error)
- func (al *AgentLoop) ProcessInstanceSelection(ctx context.Context, selection string, messages []providers.Message, ...) (*InstanceSelectionResult, error)
- func (al *AgentLoop) PublishResponseIfNeeded(ctx context.Context, channel, chatID, sessionKey, response string)
- func (al *AgentLoop) RecordLastChannel(channel string) error
- func (al *AgentLoop) RecordLastChatID(chatID string) error
- func (al *AgentLoop) RegisterTool(tool tools.Tool)
- func (al *AgentLoop) ReloadProviderAndConfig(ctx context.Context, provider providers.LLMProvider, cfg *config.Config) error
- func (al *AgentLoop) Run(ctx context.Context) error
- func (al *AgentLoop) RuntimeEventBus() runtimeevents.Bus
- func (al *AgentLoop) RuntimeEventStats() runtimeevents.Stats
- func (al *AgentLoop) RuntimeEvents() runtimeevents.EventChannel
- func (al *AgentLoop) SetChannelManager(cm *channels.Manager)
- func (al *AgentLoop) SetMediaStore(s media.MediaStore)
- func (al *AgentLoop) SetReloadFunc(fn func() error)
- func (al *AgentLoop) SetTranscriber(t asr.Transcriber)
- func (al *AgentLoop) Stop()
- func (al *AgentLoop) UnmountHook(name string)
- type AgentLoopOption
- type AgentLoopSpawner
- type AgentPromptDefinition
- type AgentRegistry
- func (r *AgentRegistry) CanSpawnSubagent(parentAgentID, targetAgentID string) bool
- func (r *AgentRegistry) Close()
- func (r *AgentRegistry) ForEachTool(name string, fn func(tools.Tool))
- func (r *AgentRegistry) GetAgent(agentID string) (*AgentInstance, bool)
- func (r *AgentRegistry) GetAgentDescriptor(agentID string) (*AgentDescriptor, bool)
- func (r *AgentRegistry) GetDefaultAgent() *AgentInstance
- func (r *AgentRegistry) ListAgentIDs() []string
- func (r *AgentRegistry) ListAgents(workspace string) []AgentDescriptor
- func (r *AgentRegistry) ListSpawnableAgents(agentID string) []AgentDescriptor
- func (r *AgentRegistry) ResolveRoute(inbound bus.InboundContext) routing.ResolvedRoute
- type ApprovalDecision
- type ApprovalRequestedPayload
- type ApprovalResolvedPayload
- type ArtifactProducedPayload
- type AssembleRequest
- type AssembleResponse
- type BuiltinHookFactory
- type CompactRequest
- type ContextBuilder
- func (cb *ContextBuilder) BuildMessagesFromPrompt(req PromptBuildRequest) []providers.Message
- func (cb *ContextBuilder) BuildSystemPrompt() string
- func (cb *ContextBuilder) BuildSystemPromptParts() []PromptPart
- func (cb *ContextBuilder) BuildSystemPromptWithCache() string
- func (cb *ContextBuilder) EstimateSystemTokens(summary string, activeSkills []string) int
- func (cb *ContextBuilder) GetSkillsInfo() map[string]any
- func (cb *ContextBuilder) InvalidateCache()
- func (cb *ContextBuilder) ListSkillNames() []string
- func (cb *ContextBuilder) LoadAgentDefinition() AgentContextDefinition
- func (cb *ContextBuilder) LoadBootstrapFiles() string
- func (cb *ContextBuilder) RegisterPromptContributor(contributor PromptContributor) error
- func (cb *ContextBuilder) RegisterPromptSource(desc PromptSourceDescriptor) error
- func (cb *ContextBuilder) ResolveActiveSkillsForContext(skillNames []string) []string
- func (cb *ContextBuilder) ResolveSkillName(name string) (string, bool)
- func (cb *ContextBuilder) WithAgentDiscovery(agentID string, discover func(agentID string) []AgentDescriptor) *ContextBuilder
- func (cb *ContextBuilder) WithSplitOnMarker(enabled bool) *ContextBuilder
- func (cb *ContextBuilder) WithToolDiscovery(useBM25, useRegex bool) *ContextBuilder
- type ContextCompressPayload
- type ContextCompressReason
- type ContextManager
- type ContextManagerFactory
- type Control
- type DispatchRequest
- func (r DispatchRequest) Channel() string
- func (r DispatchRequest) ChatID() string
- func (r DispatchRequest) MessageID() string
- func (r DispatchRequest) ModelSelection() string
- func (r DispatchRequest) ReplyToMessageID() string
- func (r DispatchRequest) SelectedModule() string
- func (r DispatchRequest) SenderID() string
- type ErrorPayload
- type FollowUpQueuedPayload
- type HookAction
- type HookDecision
- type HookManager
- func (hm *HookManager) AfterLLM(ctx context.Context, resp *LLMHookResponse) (*LLMHookResponse, HookDecision)
- func (hm *HookManager) AfterTool(ctx context.Context, result *ToolResultHookResponse) (*ToolResultHookResponse, HookDecision)
- func (hm *HookManager) ApproveTool(ctx context.Context, req *ToolApprovalRequest) ApprovalDecision
- func (hm *HookManager) BeforeLLM(ctx context.Context, req *LLMHookRequest) (*LLMHookRequest, HookDecision)
- func (hm *HookManager) BeforeTool(ctx context.Context, call *ToolCallHookRequest) (*ToolCallHookRequest, HookDecision)
- func (hm *HookManager) Close()
- func (hm *HookManager) ConfigureTimeouts(observer, interceptor, approval time.Duration)
- func (hm *HookManager) Mount(reg HookRegistration) error
- func (hm *HookManager) Unmount(name string)
- type HookMeta
- type HookRegistration
- type HookSource
- type IngestRequest
- type InstanceSelectionResult
- type InterruptKind
- type InterruptReceivedPayload
- type LLMDeltaPayload
- type LLMHookRequest
- type LLMHookResponse
- type LLMInterceptor
- type LLMPhase
- type LLMRequestPayload
- type LLMResponsePayload
- type LLMRetryPayload
- type MemoryStore
- func (ms *MemoryStore) AppendToday(content string) error
- func (ms *MemoryStore) GetMemoryContext() string
- func (ms *MemoryStore) GetRecentDailyNotes(days int) string
- func (ms *MemoryStore) ReadLongTerm() string
- func (ms *MemoryStore) ReadToday() string
- func (ms *MemoryStore) WriteLongTerm(content string) error
- type ModelResolver
- type Pipeline
- func (p *Pipeline) CallLLM(ctx context.Context, turnCtx context.Context, ts *turnState, ...) (Control, error)
- func (p *Pipeline) ExecuteTools(ctx context.Context, turnCtx context.Context, ts *turnState, ...) ToolControl
- func (p *Pipeline) Finalize(ctx context.Context, turnCtx context.Context, ts *turnState, ...) (turnResult, error)
- func (p *Pipeline) SetupTurn(ctx context.Context, ts *turnState) (*turnExecution, error)
- type ProcessHook
- func (ph *ProcessHook) AfterLLM(ctx context.Context, resp *LLMHookResponse) (*LLMHookResponse, HookDecision, error)
- func (ph *ProcessHook) AfterTool(ctx context.Context, result *ToolResultHookResponse) (*ToolResultHookResponse, HookDecision, error)
- func (ph *ProcessHook) ApproveTool(ctx context.Context, req *ToolApprovalRequest) (ApprovalDecision, error)
- func (ph *ProcessHook) BeforeLLM(ctx context.Context, req *LLMHookRequest) (*LLMHookRequest, HookDecision, error)
- func (ph *ProcessHook) BeforeTool(ctx context.Context, call *ToolCallHookRequest) (*ToolCallHookRequest, HookDecision, error)
- func (ph *ProcessHook) Close() error
- func (ph *ProcessHook) OnRuntimeEvent(ctx context.Context, evt runtimeevents.Event) error
- type ProcessHookOptions
- type PromptBuildRequest
- type PromptCachePolicy
- type PromptContributor
- type PromptLayer
- type PromptPart
- type PromptPlacement
- type PromptRegistry
- func (r *PromptRegistry) Collect(ctx context.Context, req PromptBuildRequest) ([]PromptPart, error)
- func (r *PromptRegistry) RegisterContributor(contributor PromptContributor) error
- func (r *PromptRegistry) RegisterSource(desc PromptSourceDescriptor) error
- func (r *PromptRegistry) ValidatePart(part PromptPart) error
- type PromptSlot
- type PromptSource
- type PromptSourceDescriptor
- type PromptSourceID
- type PromptStack
- type RuntimeEventObserver
- type SessionSummarizePayload
- type SkillContextSnapshot
- type SoulDefinition
- type SteeringInjectedPayload
- type SteeringMode
- type SubTurnConfig
- type SubTurnEndPayload
- type SubTurnOrphanPayload
- type SubTurnResultDeliveredPayload
- type SubTurnSpawnPayload
- type ThinkingLevel
- type Tool
- type ToolApprovalRequest
- type ToolApprover
- type ToolCallHookRequest
- type ToolControl
- type ToolExecEndPayload
- type ToolExecSkippedPayload
- type ToolExecStartPayload
- type ToolExecutionRecord
- type ToolInterceptor
- type ToolProvider
- type ToolResultHookResponse
- type TurnContext
- type TurnEndPayload
- type TurnEndStatus
- type TurnPhase
- type TurnStartPayload
- type UserDefinition
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ( ErrDepthLimitExceeded = errors.New("sub-turn depth limit exceeded") ErrConcurrencyTimeout = errors.New("timeout waiting for concurrency slot") )
Functions ¶
func EstimateMessageTokens ¶
EstimateMessageTokens estimates the token count for a single message. Delegates to the shared tokenizer package for consistency across agent and seahorse.
func EstimateToolDefsTokens ¶
func EstimateToolDefsTokens(defs []providers.ToolDefinition) int
EstimateToolDefsTokens estimates the total token cost of tool definitions as they appear in the LLM request. Delegates to the shared tokenizer package.
func RegisterBuiltinHook ¶
func RegisterBuiltinHook(name string, factory BuiltinHookFactory) error
RegisterBuiltinHook registers a named in-process hook factory for config-driven mounting.
func RegisterContextManager ¶
func RegisterContextManager(name string, factory ContextManagerFactory) error
RegisterContextManager registers a named ContextManager factory.
func SpawnSubTurn ¶
func SpawnSubTurn(ctx context.Context, cfg SubTurnConfig) (*tools.ToolResult, error)
SpawnSubTurn is the exported entry point for tools to spawn sub-turns. It retrieves AgentLoop and parent turnState from context and delegates to spawnSubTurn.
func TurnStateFromContext ¶
TurnStateFromContext retrieves turnState from context (exported for tools)
Types ¶
type ActiveTurnInfo ¶
type AgentContextDefinition ¶
type AgentContextDefinition struct {
Agent *AgentPromptDefinition `json:"agent,omitempty"`
Soul *SoulDefinition `json:"soul,omitempty"`
User *UserDefinition `json:"user,omitempty"`
}
AgentContextDefinition captures the workspace agent definition in a runtime-friendly shape.
type AgentDescriptor ¶
type AgentDescriptor struct {
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
}
AgentDescriptor is the structured discovery payload injected into each agent's system prompt so the LLM can choose a peer by identity.
type AgentFrontmatter ¶
type AgentFrontmatter struct {
Name string `json:"name"`
Description string `json:"description"`
Tools []string `json:"tools,omitempty"`
Model string `json:"model,omitempty"`
MaxTurns *int `json:"maxTurns,omitempty"`
Skills []string `json:"skills,omitempty"`
MCPServers []string `json:"mcpServers,omitempty"`
Fields map[string]any `json:"-"`
}
AgentFrontmatter holds machine-readable AGENT.md configuration.
Known fields are exposed directly for convenience. Fields keeps the full parsed frontmatter so future refactors can read additional keys without changing the loader contract again.
type AgentInstance ¶
type AgentInstance struct {
ID string
Name string
// Model is the agent's model selection — an exact target
// "instance-id/model-id" or a model route name — taken from its AGENT.md
// frontmatter, its agents.list entry or agents.defaults.model_name, in
// that order. Empty when none is set.
Model string
Workspace string
// ModuleSummaries are one-line capability summaries for each enabled
// module, composed into the system prompt so the agent knows what its
// installed modules can do without loading their full documentation.
ModuleSummaries []string
// ModuleKnowledge composes a selected module's overlay and skills for one
// turn. It is set at init, where module discovery already happens, so a
// turn never pays to re-describe every installed module.
//
// nil when no module is installed, and it returns empty for a turn with no
// selection -- an installed module costs one line of capability summary
// until someone points the agent at it.
ModuleKnowledge func(moduleID string) (overlays string, skills string, warnings []string)
MaxIterations int
MaxTokens int
Temperature float64
// ThinkingLevel is the extended-thinking level of the agent's first
// target, from its instance's runtime settings.
ThinkingLevel ThinkingLevel
ThinkingLevelConfigured bool
ContextWindow int
SummarizeMessageThreshold int
SummarizeTokenPercent int
// Provider is the provider of Candidates[0]; nil when the agent has no
// model.
Provider providers.LLMProvider
Sessions session.SessionStore
ContextBuilder *ContextBuilder
Tools *tools.ToolRegistry
Definition AgentContextDefinition
Subagents *config.SubagentsConfig
SkillsFilter []string
MCPServerAllowlist map[string]struct{}
// Candidates are the targets Model resolved to, in failover order: one
// for an exact target, the route's targets for a route. Empty when the
// agent has no model.
Candidates []providers.FallbackCandidate
// ImageCandidates are the targets of agents.defaults.image_model, which
// turns carrying images are rerouted to.
ImageCandidates []providers.FallbackCandidate
// Router is non-nil when model routing is configured and the light model
// resolved. It scores each incoming message and decides whether to route
// to LightCandidates or stay with Candidates.
Router *routing.Router
// LightCandidates are the targets of the routing light model selection,
// resolved at agent creation.
LightCandidates []providers.FallbackCandidate
// LightProvider is the provider of LightCandidates[0]. It is only used
// when routing selects the light tier for a turn.
LightProvider providers.LLMProvider
// CandidateProviders and CandidateConfigs hold the provider and the
// request spec of every candidate above, keyed by
// FallbackCandidate.StableKey(). A request spec carries the target
// instance's runtime settings: thinking level, streaming, RPM, timeouts.
CandidateProviders map[string]providers.LLMProvider
CandidateConfigs map[string]*providers.CallSpec
// contains filtered or unexported fields
}
AgentInstance represents a fully configured agent with its own workspace, session manager, context builder, and tool registry.
func NewAgentInstance ¶
func NewAgentInstance( agentCfg *config.AgentConfig, defaults *config.AgentDefaults, cfg *config.Config, provider providers.LLMProvider, resolve ModelResolver, ) *AgentInstance
NewAgentInstance creates an agent instance from config. The agent runs on its model selection (see AgentInstance.Model), resolved through resolve; when provider is not nil it serves the default selection instead, with no resolution. A nil resolve uses the default model resolver.
func (*AgentInstance) AllowsMCPServer ¶
func (a *AgentInstance) AllowsMCPServer(serverName string) bool
func (*AgentInstance) Close ¶
func (a *AgentInstance) Close() error
Close releases resources held by the agent's providers and session store.
type AgentLoop ¶
type AgentLoop struct {
// contains filtered or unexported fields
}
func AgentLoopFromContext ¶
AgentLoopFromContext retrieves AgentLoop from context
func NewAgentLoop ¶
func NewAgentLoop( cfg *config.Config, msgBus *bus.MessageBus, provider providers.LLMProvider, opts ...AgentLoopOption, ) *AgentLoop
NewAgentLoop creates the agent loop over cfg. Each agent runs on its model selection, resolved through the loop's model resolver (see WithModelResolver). provider, when not nil, serves the default selection instead, as a single candidate and with no resolution — the seam embedders and tests inject a provider through. An agent whose selection is empty or does not resolve has no model: its turns without a per-message selection fail with a no-model error.
Example ¶
ExampleNewAgentLoop runs one turn on the models connected in Compa.
package main
import (
"context"
"fmt"
"path/filepath"
"time"
"github.com/xibodev/compa/pkg/agent"
"github.com/xibodev/compa/pkg/bus"
"github.com/xibodev/compa/pkg/config"
"github.com/xibodev/compa/pkg/session"
)
func main() {
// The config the Compa web UI saves: $COMPA_HOME/config.json, by default
// ~/.compa/config.json. A missing file loads the defaults, which connect
// no provider.
cfg, err := config.LoadConfig(filepath.Join(config.GetHome(), "config.json"))
if err != nil {
fmt.Println("load config:", err)
return
}
// Optional: run on another model than the default model.
// cfg.Agents.Defaults.ModelName = "openai/gpt-5.4" // exact target: instance-id/model-id
// cfg.Agents.Defaults.ModelName = "everyday" // or the name of a model route
msgBus := bus.NewMessageBus()
defer msgBus.Close()
// A nil provider resolves the model selection against the providers
// connected in Compa.
loop := agent.NewAgentLoop(cfg, msgBus, nil)
defer loop.Close()
if _, err := loop.DefaultModelStatus(); err != nil {
fmt.Println(err) // for example: no default model is selected
return
}
// Turns with the same key share one conversation. The loop keeps a key
// the caller supplies only when it is opaque, so build it with
// session.BuildOpaqueSessionKey.
sessionKey := session.BuildOpaqueSessionKey("my-app:demo")
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
defer cancel()
reply, err := loop.ProcessDirect(ctx, "Say hello in five words.", sessionKey)
if err != nil {
fmt.Println("turn failed:", err)
return
}
fmt.Println(reply)
}
Output:
func (*AgentLoop) Close ¶
func (al *AgentLoop) Close()
Close releases resources held by agent session stores. Call after Stop.
func (*AgentLoop) Continue ¶
func (al *AgentLoop) Continue(ctx context.Context, sessionKey, channel, chatID string) (string, error)
Continue resumes an idle agent by dequeuing any pending steering messages and running them through the agent loop. This is used when the agent's last message was from the assistant (i.e., it has stopped processing) and the user has since enqueued steering messages.
If no steering messages are pending, it returns an empty string.
func (*AgentLoop) DefaultModelStatus ¶
DefaultModelStatus reports the default agent's model selection and, when the agent has no model to run on, why: every turn without a per-message selection then fails with that error.
func (*AgentLoop) GetActiveTurnBySession ¶
func (al *AgentLoop) GetActiveTurnBySession(sessionKey string) *ActiveTurnInfo
func (*AgentLoop) GetRegistry ¶
func (al *AgentLoop) GetRegistry() *AgentRegistry
func (*AgentLoop) GetStartupInfo ¶
func (*AgentLoop) HardAbort ¶
HardAbort immediately cancels the running agent loop for the given session, cascading the cancellation to all child SubTurns. This is a destructive operation that terminates execution without waiting for graceful cleanup.
Use this when the user explicitly requests immediate termination (e.g., "stop now", "abort"). To redirect a running turn instead, deliver another inbound message for the session: the agent loop queues it as steering for the active turn.
func (*AgentLoop) MountHook ¶
func (al *AgentLoop) MountHook(reg HookRegistration) error
MountHook registers an in-process hook on the agent loop.
func (*AgentLoop) MountProcessHook ¶
func (*AgentLoop) ProcessDirect ¶
func (*AgentLoop) ProcessDirectWithChannel ¶
func (*AgentLoop) ProcessHeartbeat ¶
func (*AgentLoop) ProcessInstanceSelection ¶
func (al *AgentLoop) ProcessInstanceSelection( ctx context.Context, selection string, messages []providers.Message, tools []providers.ToolDefinition, options map[string]any, onChunk func(string), ) (*InstanceSelectionResult, error)
ProcessInstanceSelection invokes one explicit instance target or named route with the given messages, outside any turn or session: the selection resolves against the loop's current config, its targets run in order through core's candidate executor (providers.Failover), and a streaming target that fails after visible output does not fall over.
func (*AgentLoop) PublishResponseIfNeeded ¶
func (*AgentLoop) RecordLastChannel ¶
func (*AgentLoop) RecordLastChatID ¶
func (*AgentLoop) RegisterTool ¶
func (*AgentLoop) ReloadProviderAndConfig ¶
func (al *AgentLoop) ReloadProviderAndConfig( ctx context.Context, provider providers.LLMProvider, cfg *config.Config, ) error
ReloadProviderAndConfig atomically swaps the config and rebuilds every agent from it: each agent resolves its model selection anew, so a changed default model, route or provider instance takes effect. provider, when not nil, serves the default selection instead of a resolution, as in NewAgentLoop. It uses a context to allow timeout control from the caller. Returns an error if the reload fails or context is canceled.
func (*AgentLoop) RuntimeEventBus ¶
func (al *AgentLoop) RuntimeEventBus() runtimeevents.Bus
RuntimeEventBus returns the runtime event bus used by the agent loop.
func (*AgentLoop) RuntimeEventStats ¶
func (al *AgentLoop) RuntimeEventStats() runtimeevents.Stats
RuntimeEventStats returns runtime event bus counters.
func (*AgentLoop) RuntimeEvents ¶
func (al *AgentLoop) RuntimeEvents() runtimeevents.EventChannel
RuntimeEvents returns the root runtime event channel.
func (*AgentLoop) SetChannelManager ¶
func (*AgentLoop) SetMediaStore ¶
func (al *AgentLoop) SetMediaStore(s media.MediaStore)
func (*AgentLoop) SetReloadFunc ¶
func (*AgentLoop) SetTranscriber ¶
func (al *AgentLoop) SetTranscriber(t asr.Transcriber)
func (*AgentLoop) UnmountHook ¶
UnmountHook removes a previously registered in-process hook.
type AgentLoopOption ¶
type AgentLoopOption func(*AgentLoop)
AgentLoopOption configures an AgentLoop at construction time.
func WithModelResolver ¶
func WithModelResolver(resolve ModelResolver) AgentLoopOption
WithModelResolver sets how the loop resolves model selections: each agent's model, the image and light models, /switch model, hook model rewrites and per-message selections, always against the loop's current config. nil keeps the default, modelservice.NewResolver().Resolve, which reads the saved catalogs and the auth store.
func WithRuntimeEvents ¶
func WithRuntimeEvents(bus runtimeevents.Bus) AgentLoopOption
WithRuntimeEvents injects the runtime event bus used for new observation APIs.
The injected bus is treated as externally owned and will not be closed by AgentLoop.Close. Passing nil leaves the default owned runtime bus enabled.
func WithToolProviders ¶
func WithToolProviders(providers ...ToolProvider) AgentLoopOption
WithToolProviders injects capability providers at construction time.
Called once per agent during shared-tool registration, in the order given. Summaries accumulate across providers; the LAST non-nil knowledge loader wins, because a single selection resolves to a single provider today and silently merging two loaders would make an ambiguous selection look decided.
Passing none is fully supported and is the standalone-kernel case.
Example ¶
ExampleWithToolProviders adds the host program's own tools to the agent.
package main
import (
"context"
"fmt"
"path/filepath"
"time"
"github.com/xibodev/compa/pkg/agent"
"github.com/xibodev/compa/pkg/bus"
"github.com/xibodev/compa/pkg/config"
"github.com/xibodev/compa/pkg/session"
"github.com/xibodev/compa/pkg/tools"
)
// hostClock is a tool the host program provides. The agent calls it like a
// built-in tool.
type hostClock struct{}
func (hostClock) Name() string { return "host_time" }
func (hostClock) Description() string { return "Returns the current date and time on this computer." }
func (hostClock) Parameters() map[string]any {
return map[string]any{"type": "object", "properties": map[string]any{}}
}
func (hostClock) Execute(context.Context, map[string]any) *tools.ToolResult {
return tools.NewToolResult(time.Now().Format(time.RFC1123))
}
// hostTools gives every agent of the loop the host's tools.
type hostTools struct{}
func (hostTools) RegisterTools(
workspace string,
register func(agent.Tool),
) ([]string, func(string) (string, string, []string)) {
register(hostClock{})
return nil, nil
}
func main() {
cfg, err := config.LoadConfig(filepath.Join(config.GetHome(), "config.json"))
if err != nil {
fmt.Println("load config:", err)
return
}
msgBus := bus.NewMessageBus()
defer msgBus.Close()
loop := agent.NewAgentLoop(cfg, msgBus, nil, agent.WithToolProviders(hostTools{}))
defer loop.Close()
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
defer cancel()
reply, err := loop.ProcessDirect(ctx, "What time is it?", session.BuildOpaqueSessionKey("my-app:demo"))
if err != nil {
fmt.Println("turn failed:", err)
return
}
fmt.Println(reply)
}
Output:
type AgentLoopSpawner ¶
type AgentLoopSpawner struct {
// contains filtered or unexported fields
}
AgentLoopSpawner implements tools.SubTurnSpawner interface. This allows tools to spawn sub-turns without circular dependency.
func NewSubTurnSpawner ¶
func NewSubTurnSpawner(al *AgentLoop) *AgentLoopSpawner
NewSubTurnSpawner creates a SubTurnSpawner for the given AgentLoop.
func (*AgentLoopSpawner) SpawnSubTurn ¶
func (s *AgentLoopSpawner) SpawnSubTurn( ctx context.Context, cfg tools.SubTurnConfig, ) (*tools.ToolResult, error)
SpawnSubTurn implements tools.SubTurnSpawner interface.
type AgentPromptDefinition ¶
type AgentPromptDefinition struct {
Path string `json:"path"`
Raw string `json:"raw"`
Body string `json:"body"`
RawFrontmatter string `json:"raw_frontmatter,omitempty"`
Frontmatter AgentFrontmatter `json:"frontmatter"`
FrontmatterErr string `json:"frontmatter_error,omitempty"`
}
AgentPromptDefinition represents the parsed AGENT.md prompt file.
type AgentRegistry ¶
type AgentRegistry struct {
// contains filtered or unexported fields
}
AgentRegistry manages multiple agent instances and routes messages to them.
func NewAgentRegistry ¶
func NewAgentRegistry( cfg *config.Config, provider providers.LLMProvider, resolve ModelResolver, ) *AgentRegistry
NewAgentRegistry creates a registry from config, instantiating all agents. Each agent resolves its model selection through resolve; provider, when not nil, serves the default selection instead (see NewAgentInstance).
func (*AgentRegistry) CanSpawnSubagent ¶
func (r *AgentRegistry) CanSpawnSubagent(parentAgentID, targetAgentID string) bool
CanSpawnSubagent checks if parentAgentID is allowed to spawn targetAgentID.
func (*AgentRegistry) Close ¶
func (r *AgentRegistry) Close()
Close releases resources held by all registered agents.
func (*AgentRegistry) ForEachTool ¶
func (r *AgentRegistry) ForEachTool(name string, fn func(tools.Tool))
ForEachTool calls fn for every tool registered under the given name across all agents. This is useful for propagating dependencies (e.g. MediaStore) to tools after registry construction.
func (*AgentRegistry) GetAgent ¶
func (r *AgentRegistry) GetAgent(agentID string) (*AgentInstance, bool)
GetAgent returns the agent instance for a given ID.
func (*AgentRegistry) GetAgentDescriptor ¶
func (r *AgentRegistry) GetAgentDescriptor(agentID string) (*AgentDescriptor, bool)
GetAgentDescriptor returns the structured discovery payload for one agent.
func (*AgentRegistry) GetDefaultAgent ¶
func (r *AgentRegistry) GetDefaultAgent() *AgentInstance
GetDefaultAgent returns the default agent instance.
func (*AgentRegistry) ListAgentIDs ¶
func (r *AgentRegistry) ListAgentIDs() []string
ListAgentIDs returns all registered agent IDs.
func (*AgentRegistry) ListAgents ¶
func (r *AgentRegistry) ListAgents(workspace string) []AgentDescriptor
ListAgents returns structured descriptors for every agent in the current Compa instance. The current workspace, when provided, is used only to order the matching agent first for prompt readability.
func (*AgentRegistry) ListSpawnableAgents ¶
func (r *AgentRegistry) ListSpawnableAgents(agentID string) []AgentDescriptor
ListSpawnableAgents returns descriptors only when the current agent can call spawn, and only for peers it is allowed to spawn. Restricted peers are intentionally omitted from discovery.
func (*AgentRegistry) ResolveRoute ¶
func (r *AgentRegistry) ResolveRoute(inbound bus.InboundContext) routing.ResolvedRoute
ResolveRoute determines which agent handles the normalized inbound context.
type ApprovalDecision ¶
type ApprovalRequestedPayload ¶
type ApprovalRequestedPayload struct {
ApprovalID string `json:"approval_id"`
ActionID string `json:"action_id"`
Tool string `json:"tool"`
Parameters map[string]any `json:"parameters,omitempty"`
Reason string `json:"reason"`
State string `json:"state"` // "pending", "approved", "rejected", "cancelled"
}
ApprovalRequestedPayload describes a required human approval.
type ApprovalResolvedPayload ¶
type ApprovalResolvedPayload struct {
ApprovalID string `json:"approval_id"`
Resolution string `json:"resolution"` // "approved", "rejected", "cancelled"
Reason string `json:"reason,omitempty"`
}
ApprovalResolvedPayload describes human approval resolution.
type ArtifactProducedPayload ¶
type ArtifactProducedPayload struct {
ArtifactID string `json:"artifact_id"`
Kind string `json:"kind"`
Module string `json:"module"`
Locator string `json:"locator"`
MediaType string `json:"media_type"`
Bytes int64 `json:"bytes"`
Digest string `json:"digest"`
Title string `json:"title,omitempty"`
ReviewState string `json:"review_state,omitempty"` // "draft", "verified", "reviewed", "approved"
ToolName string `json:"tool_name,omitempty"`
}
ArtifactProducedPayload describes an authoritative output emitted by a tool or operation.
type AssembleRequest ¶
type AssembleRequest struct {
SessionKey string // session identifier
Budget int // context window in tokens
MaxTokens int // max response tokens
}
AssembleRequest is the input to Assemble.
type AssembleResponse ¶
type AssembleResponse struct {
History []providers.Message // assembled conversation history for BuildMessagesFromPrompt
Summary string // conversation summary embedded into system prompt by BuildMessagesFromPrompt
}
AssembleResponse is the output of Assemble.
type BuiltinHookFactory ¶
BuiltinHookFactory constructs an in-process hook from config.
type CompactRequest ¶
type CompactRequest struct {
SessionKey string // session identifier
Reason ContextCompressReason // proactive_budget | llm_retry | summarize
Budget int // context window budget (used for retry aggressive compaction)
}
CompactRequest is the input to Compact.
type ContextBuilder ¶
type ContextBuilder struct {
// contains filtered or unexported fields
}
func NewContextBuilder ¶
func NewContextBuilder(workspace string) *ContextBuilder
func (*ContextBuilder) BuildMessagesFromPrompt ¶
func (cb *ContextBuilder) BuildMessagesFromPrompt(req PromptBuildRequest) []providers.Message
func (*ContextBuilder) BuildSystemPrompt ¶
func (cb *ContextBuilder) BuildSystemPrompt() string
func (*ContextBuilder) BuildSystemPromptParts ¶
func (cb *ContextBuilder) BuildSystemPromptParts() []PromptPart
func (*ContextBuilder) BuildSystemPromptWithCache ¶
func (cb *ContextBuilder) BuildSystemPromptWithCache() string
BuildSystemPromptWithCache returns the cached system prompt if available and source files haven't changed, otherwise builds and caches it. Source file changes are detected via mtime checks (cheap stat calls).
func (*ContextBuilder) EstimateSystemTokens ¶
func (cb *ContextBuilder) EstimateSystemTokens(summary string, activeSkills []string) int
EstimateSystemTokens estimates the token count of the full system message that would be sent to the LLM, mirroring the composition logic in BuildMessagesFromPrompt. It includes: static prompt, dynamic context, active skills, and summary with wrapping prefixes and separators. This avoids needing all per-request parameters that BuildMessagesFromPrompt requires (media, channel, chatID, sender, etc.).
func (*ContextBuilder) GetSkillsInfo ¶
func (cb *ContextBuilder) GetSkillsInfo() map[string]any
GetSkillsInfo returns information about loaded skills.
func (*ContextBuilder) InvalidateCache ¶
func (cb *ContextBuilder) InvalidateCache()
InvalidateCache clears the cached system prompt. Normally not needed because the cache auto-invalidates via mtime checks, but this is useful for tests or explicit reload commands.
func (*ContextBuilder) ListSkillNames ¶
func (cb *ContextBuilder) ListSkillNames() []string
func (*ContextBuilder) LoadAgentDefinition ¶
func (cb *ContextBuilder) LoadAgentDefinition() AgentContextDefinition
LoadAgentDefinition parses the workspace agent definition files: AGENT.md, SOUL.md and USER.md. Each is optional.
func (*ContextBuilder) LoadBootstrapFiles ¶
func (cb *ContextBuilder) LoadBootstrapFiles() string
func (*ContextBuilder) RegisterPromptContributor ¶
func (cb *ContextBuilder) RegisterPromptContributor(contributor PromptContributor) error
func (*ContextBuilder) RegisterPromptSource ¶
func (cb *ContextBuilder) RegisterPromptSource(desc PromptSourceDescriptor) error
func (*ContextBuilder) ResolveActiveSkillsForContext ¶
func (cb *ContextBuilder) ResolveActiveSkillsForContext(skillNames []string) []string
func (*ContextBuilder) ResolveSkillName ¶
func (cb *ContextBuilder) ResolveSkillName(name string) (string, bool)
func (*ContextBuilder) WithAgentDiscovery ¶
func (cb *ContextBuilder) WithAgentDiscovery( agentID string, discover func(agentID string) []AgentDescriptor, ) *ContextBuilder
func (*ContextBuilder) WithSplitOnMarker ¶
func (cb *ContextBuilder) WithSplitOnMarker(enabled bool) *ContextBuilder
func (*ContextBuilder) WithToolDiscovery ¶
func (cb *ContextBuilder) WithToolDiscovery(useBM25, useRegex bool) *ContextBuilder
type ContextCompressPayload ¶
type ContextCompressPayload struct {
Reason ContextCompressReason
DroppedMessages int
RemainingMessages int
}
ContextCompressPayload describes a forced history compression.
type ContextCompressReason ¶
type ContextCompressReason string
ContextCompressReason identifies why emergency compression ran.
const ( // ContextCompressReasonProactive indicates compression before the first LLM call. ContextCompressReasonProactive ContextCompressReason = "proactive_budget" // ContextCompressReasonRetry indicates compression during context-error retry handling. ContextCompressReasonRetry ContextCompressReason = "llm_retry" // ContextCompressReasonSummarize indicates post-turn async summarization. ContextCompressReasonSummarize ContextCompressReason = "summarize" )
type ContextManager ¶
type ContextManager interface {
// Assemble builds budget-aware context from the ContextManager's own storage.
// Called before BuildMessagesFromPrompt. Returns assembled messages ready for LLM.
Assemble(ctx context.Context, req *AssembleRequest) (*AssembleResponse, error)
// Compact compresses conversation history.
// Called after turn completes (may be async internally) and on context overflow (sync).
Compact(ctx context.Context, req *CompactRequest) error
// Ingest records a message into the ContextManager's own storage.
// Called after each message is persisted to session JSONL.
Ingest(ctx context.Context, req *IngestRequest) error
// Clear removes all stored context for a session (messages, summaries, etc.).
// Called when the user issues /clear or /reset.
Clear(ctx context.Context, sessionKey string) error
}
ContextManager manages conversation context via a pluggable strategy. Exactly ONE ContextManager is active per AgentLoop, selected by config. The built-in default summarizes and compresses the session history in place.
type ContextManagerFactory ¶
type ContextManagerFactory func(cfg json.RawMessage, al *AgentLoop) (ContextManager, error)
ContextManagerFactory constructs a ContextManager from config. al provides access to the AgentLoop's runtime resources (provider, model, workspace, etc.) cfg is the raw JSON configuration from config.json (may be nil).
type Control ¶
type Control int
const ( // ControlContinue tells the coordinator to jump back to the top of the turn loop // (equivalent to the original "goto turnLoop"). ControlContinue Control = iota // ControlBreak tells the coordinator to exit the turn loop and proceed to Finalize. ControlBreak // ControlToolLoop tells the coordinator to execute the tool loop. ControlToolLoop )
type DispatchRequest ¶
type DispatchRequest struct {
SessionKey string
InboundContext *bus.InboundContext
RouteResult *routing.ResolvedRoute
SessionScope *session.SessionScope
UserMessage string
Media []string
}
DispatchRequest is the normalized runtime input passed into the agent loop after routing and session allocation have completed.
func (DispatchRequest) Channel ¶
func (r DispatchRequest) Channel() string
func (DispatchRequest) ChatID ¶
func (r DispatchRequest) ChatID() string
func (DispatchRequest) MessageID ¶
func (r DispatchRequest) MessageID() string
func (DispatchRequest) ModelSelection ¶
func (r DispatchRequest) ModelSelection() string
ModelSelection is the model selection the message chose — an exact target or a model route name — or empty when it runs on the agent's model.
func (DispatchRequest) ReplyToMessageID ¶
func (r DispatchRequest) ReplyToMessageID() string
func (DispatchRequest) SelectedModule ¶
func (r DispatchRequest) SelectedModule() string
SelectedModule is the module the user pointed the agent at for this turn.
Empty when no selection was made, which is the common case: an installed module contributes its capability summaries always, and its overlay and skills only when someone asks for it.
func (DispatchRequest) SenderID ¶
func (r DispatchRequest) SenderID() string
type ErrorPayload ¶
ErrorPayload describes an execution error inside the agent loop.
type FollowUpQueuedPayload ¶
FollowUpQueuedPayload describes an async follow-up queued back into the inbound bus.
type HookAction ¶
type HookAction string
const ( HookActionContinue HookAction = "continue" HookActionModify HookAction = "modify" HookActionRespond HookAction = "respond" // Return result directly, skip tool execution. SECURITY: This bypasses ApproveTool checks, allowing hooks to return results for any tool (including sensitive ones like bash) without approval. Use with caution. HookActionDenyTool HookAction = "deny_tool" HookActionAbortTurn HookAction = "abort_turn" HookActionHardAbort HookAction = "hard_abort" )
type HookDecision ¶
type HookDecision struct {
Action HookAction `json:"action"`
Reason string `json:"reason,omitempty"`
}
type HookManager ¶
type HookManager struct {
// contains filtered or unexported fields
}
func NewHookManager ¶
func NewHookManager(runtimeEvents runtimeevents.EventChannel) *HookManager
func (*HookManager) AfterLLM ¶
func (hm *HookManager) AfterLLM(ctx context.Context, resp *LLMHookResponse) (*LLMHookResponse, HookDecision)
func (*HookManager) AfterTool ¶
func (hm *HookManager) AfterTool( ctx context.Context, result *ToolResultHookResponse, ) (*ToolResultHookResponse, HookDecision)
func (*HookManager) ApproveTool ¶
func (hm *HookManager) ApproveTool(ctx context.Context, req *ToolApprovalRequest) ApprovalDecision
func (*HookManager) BeforeLLM ¶
func (hm *HookManager) BeforeLLM(ctx context.Context, req *LLMHookRequest) (*LLMHookRequest, HookDecision)
func (*HookManager) BeforeTool ¶
func (hm *HookManager) BeforeTool( ctx context.Context, call *ToolCallHookRequest, ) (*ToolCallHookRequest, HookDecision)
func (*HookManager) Close ¶
func (hm *HookManager) Close()
func (*HookManager) ConfigureTimeouts ¶
func (hm *HookManager) ConfigureTimeouts(observer, interceptor, approval time.Duration)
func (*HookManager) Mount ¶
func (hm *HookManager) Mount(reg HookRegistration) error
func (*HookManager) Unmount ¶
func (hm *HookManager) Unmount(name string)
type HookMeta ¶
type HookMeta struct {
AgentID string
TurnID string
ParentTurnID string
SessionKey string
Iteration int
TracePath string
Source string
// contains filtered or unexported fields
}
HookMeta contains correlation fields shared by agent hook requests and runtime events emitted from turn processing.
type HookRegistration ¶
type HookRegistration struct {
Name string
Priority int
Source HookSource
Hook any
}
func NamedHook ¶
func NamedHook(name string, hook any) HookRegistration
type HookSource ¶
type HookSource uint8
const ( HookSourceInProcess HookSource = iota HookSourceProcess )
type IngestRequest ¶
type IngestRequest struct {
SessionKey string // session identifier
Message providers.Message // the message just persisted
}
IngestRequest is the input to Ingest.
type InstanceSelectionResult ¶
type InstanceSelectionResult struct {
Response *providers.LLMResponse
ServedIdentity string
ExactTarget string
}
type InterruptKind ¶
type InterruptKind string
const (
InterruptKindSteering InterruptKind = "steering"
)
type InterruptReceivedPayload ¶
type InterruptReceivedPayload struct {
Kind InterruptKind
Role string
ContentLen int
QueueDepth int
}
InterruptReceivedPayload describes accepted turn-control input.
type LLMDeltaPayload ¶
LLMDeltaPayload describes a streamed LLM delta.
type LLMHookRequest ¶
type LLMHookRequest struct {
Meta HookMeta `json:"meta"`
Context *TurnContext `json:"context,omitempty"`
Model string `json:"model"`
Messages []providers.Message `json:"messages,omitempty"`
Tools []providers.ToolDefinition `json:"tools,omitempty"`
Options map[string]any `json:"options,omitempty"`
}
func (*LLMHookRequest) Clone ¶
func (r *LLMHookRequest) Clone() *LLMHookRequest
type LLMHookResponse ¶
type LLMHookResponse struct {
Meta HookMeta `json:"meta"`
Context *TurnContext `json:"context,omitempty"`
Model string `json:"model"`
Response *providers.LLMResponse `json:"response,omitempty"`
}
func (*LLMHookResponse) Clone ¶
func (r *LLMHookResponse) Clone() *LLMHookResponse
type LLMInterceptor ¶
type LLMInterceptor interface {
BeforeLLM(ctx context.Context, req *LLMHookRequest) (*LLMHookRequest, HookDecision, error)
AfterLLM(ctx context.Context, resp *LLMHookResponse) (*LLMHookResponse, HookDecision, error)
}
type LLMRequestPayload ¶
type LLMRequestPayload struct {
Model string
MessagesCount int
ToolsCount int
MaxTokens int
Temperature float64
}
LLMRequestPayload describes an outbound LLM request.
type LLMResponsePayload ¶
LLMResponsePayload describes an inbound LLM response.
type LLMRetryPayload ¶
type LLMRetryPayload struct {
Attempt int
MaxRetries int
Reason string
Error string
Backoff time.Duration
}
LLMRetryPayload describes a retry of an LLM request.
type MemoryStore ¶
type MemoryStore struct {
// contains filtered or unexported fields
}
MemoryStore manages persistent memory for the agent. - Long-term memory: memory/MEMORY.md - Daily notes: memory/YYYYMM/YYYYMMDD.md
func NewMemoryStore ¶
func NewMemoryStore(workspace string) *MemoryStore
NewMemoryStore creates a new MemoryStore with the given workspace path. It ensures the memory directory exists.
func (*MemoryStore) AppendToday ¶
func (ms *MemoryStore) AppendToday(content string) error
AppendToday appends content to today's daily note. If the file doesn't exist, it creates a new file with a date header.
func (*MemoryStore) GetMemoryContext ¶
func (ms *MemoryStore) GetMemoryContext() string
GetMemoryContext returns formatted memory context for the agent prompt. Includes long-term memory and recent daily notes.
func (*MemoryStore) GetRecentDailyNotes ¶
func (ms *MemoryStore) GetRecentDailyNotes(days int) string
GetRecentDailyNotes returns daily notes from the last N days. Contents are joined with "---" separator.
func (*MemoryStore) ReadLongTerm ¶
func (ms *MemoryStore) ReadLongTerm() string
ReadLongTerm reads the long-term memory (MEMORY.md). Returns empty string if the file doesn't exist.
func (*MemoryStore) ReadToday ¶
func (ms *MemoryStore) ReadToday() string
ReadToday reads today's daily note. Returns empty string if the file doesn't exist.
func (*MemoryStore) WriteLongTerm ¶
func (ms *MemoryStore) WriteLongTerm(content string) error
WriteLongTerm writes content to the long-term memory file (MEMORY.md).
type ModelResolver ¶
type ModelResolver func(cfg *config.Config, selection string) (*providers.InstanceResolution, error)
ModelResolver resolves a model selection — an exact target "instance-id/model-id" or a model route name — against cfg into ordered candidates, each running on its own provider instance. The loop resolves every selection through it: the agents' models, the image and light models, /switch model, hook rewrites and per-message selections.
type Pipeline ¶
type Pipeline struct {
Bus interfaces.MessageBus
Cfg *config.Config
ContextManager ContextManager
Hooks *HookManager
Failover *providers.Failover
ChannelManager interfaces.ChannelManager
MediaStore media.MediaStore
Steering any // TODO: *Steering
// contains filtered or unexported fields
}
Pipeline holds the runtime dependencies used by Pipeline methods. It is constructed by runTurn via NewPipeline and passed to sub-methods so that the coordinator can delegate phase execution.
func NewPipeline ¶
NewPipeline creates a Pipeline from an AgentLoop instance.
func (*Pipeline) CallLLM ¶
func (p *Pipeline) CallLLM( ctx context.Context, turnCtx context.Context, ts *turnState, exec *turnExecution, iteration int, ) (Control, error)
CallLLM performs an LLM call with fallback support, hook invocation, and retry logic. It handles PreLLM setup, the actual LLM invocation with retry, and AfterLLM processing. Returns Control indicating what the coordinator should do next.
func (*Pipeline) ExecuteTools ¶
func (p *Pipeline) ExecuteTools( ctx context.Context, turnCtx context.Context, ts *turnState, exec *turnExecution, iteration int, ) ToolControl
ExecuteTools executes the tool loop, handling BeforeTool/ApproveTool/AfterTool hooks, tool execution with async callbacks, media delivery, and steering injection. Returns ToolControl indicating what the coordinator should do next:
- ToolControlContinue: all tool results handled, pendingMessages or steering exists, continue turn
- ToolControlBreak: tool loop exited, proceed to coordinator's hardAbort/finalContent/finalize
func (*Pipeline) Finalize ¶
func (p *Pipeline) Finalize( ctx context.Context, turnCtx context.Context, ts *turnState, exec *turnExecution, turnStatus TurnEndStatus, finalContent string, ) (turnResult, error)
Finalize handles turn finalization, either: - Early return when allResponsesHandled=true (ExecuteTools already finalized) - Normal finalization for allResponsesHandled=false (sets finalContent, saves session, compact)
func (*Pipeline) SetupTurn ¶
SetupTurn extracts the one-time initialization phase, returning a turnExecution populated with history, messages, and candidate selection. A turn runs on the message's own model selection when it has one, and on the agent's model otherwise; with neither it fails with a no-model error before anything is recorded.
type ProcessHook ¶
type ProcessHook struct {
// contains filtered or unexported fields
}
func NewProcessHook ¶
func NewProcessHook(ctx context.Context, name string, opts ProcessHookOptions) (*ProcessHook, error)
func (*ProcessHook) AfterLLM ¶
func (ph *ProcessHook) AfterLLM( ctx context.Context, resp *LLMHookResponse, ) (*LLMHookResponse, HookDecision, error)
func (*ProcessHook) AfterTool ¶
func (ph *ProcessHook) AfterTool( ctx context.Context, result *ToolResultHookResponse, ) (*ToolResultHookResponse, HookDecision, error)
func (*ProcessHook) ApproveTool ¶
func (ph *ProcessHook) ApproveTool(ctx context.Context, req *ToolApprovalRequest) (ApprovalDecision, error)
func (*ProcessHook) BeforeLLM ¶
func (ph *ProcessHook) BeforeLLM( ctx context.Context, req *LLMHookRequest, ) (*LLMHookRequest, HookDecision, error)
func (*ProcessHook) BeforeTool ¶
func (ph *ProcessHook) BeforeTool( ctx context.Context, call *ToolCallHookRequest, ) (*ToolCallHookRequest, HookDecision, error)
func (*ProcessHook) Close ¶
func (ph *ProcessHook) Close() error
func (*ProcessHook) OnRuntimeEvent ¶
func (ph *ProcessHook) OnRuntimeEvent(ctx context.Context, evt runtimeevents.Event) error
type ProcessHookOptions ¶
type PromptBuildRequest ¶
type PromptBuildRequest struct {
History []providers.Message
Summary string
CurrentMessage string
Media []string
Channel string
ChatID string
SenderID string
SenderDisplayName string
ActiveSkills []string
Overlays []PromptPart
SuppressDefaultSystemPrompt bool
SuppressSkillContext bool
// ModuleSummaries are one-line capability summaries for the enabled
// detached modules, carried per turn so the agent's context reflects what
// is installed right now rather than what was installed at startup.
ModuleSummaries []string
SuppressToolUseRule bool
AllowedSkills []string
AllowedTools []string
ToolUseFallback bool
}
type PromptCachePolicy ¶
type PromptCachePolicy string
const ( PromptCacheDefault PromptCachePolicy = "" PromptCacheEphemeral PromptCachePolicy = "ephemeral" PromptCacheNone PromptCachePolicy = "none" )
type PromptContributor ¶
type PromptContributor interface {
PromptSource() PromptSourceDescriptor
ContributePrompt(ctx context.Context, req PromptBuildRequest) ([]PromptPart, error)
}
type PromptLayer ¶
type PromptLayer string
const ( PromptLayerKernel PromptLayer = "kernel" PromptLayerInstruction PromptLayer = "instruction" PromptLayerCapability PromptLayer = "capability" PromptLayerContext PromptLayer = "context" PromptLayerTurn PromptLayer = "turn" )
type PromptPart ¶
type PromptPart struct {
ID string
Layer PromptLayer
Slot PromptSlot
Source PromptSource
Title string
Content string
Stable bool
Cache PromptCachePolicy
}
type PromptPlacement ¶
type PromptPlacement struct {
Layer PromptLayer
Slot PromptSlot
}
type PromptRegistry ¶
type PromptRegistry struct {
// contains filtered or unexported fields
}
func NewPromptRegistry ¶
func NewPromptRegistry() *PromptRegistry
func (*PromptRegistry) Collect ¶
func (r *PromptRegistry) Collect(ctx context.Context, req PromptBuildRequest) ([]PromptPart, error)
func (*PromptRegistry) RegisterContributor ¶
func (r *PromptRegistry) RegisterContributor(contributor PromptContributor) error
func (*PromptRegistry) RegisterSource ¶
func (r *PromptRegistry) RegisterSource(desc PromptSourceDescriptor) error
func (*PromptRegistry) ValidatePart ¶
func (r *PromptRegistry) ValidatePart(part PromptPart) error
type PromptSlot ¶
type PromptSlot string
const ( PromptSlotIdentity PromptSlot = "identity" PromptSlotHierarchy PromptSlot = "hierarchy" PromptSlotWorkspace PromptSlot = "workspace" PromptSlotTooling PromptSlot = "tooling" PromptSlotMCP PromptSlot = "mcp" PromptSlotSkillCatalog PromptSlot = "skill_catalog" PromptSlotActiveSkill PromptSlot = "active_skill" PromptSlotMemory PromptSlot = "memory" PromptSlotRuntime PromptSlot = "runtime" PromptSlotSummary PromptSlot = "summary" PromptSlotMessage PromptSlot = "message" PromptSlotSteering PromptSlot = "steering" PromptSlotSubTurn PromptSlot = "subturn" PromptSlotToolResult PromptSlot = "tool_result" PromptSlotOutput PromptSlot = "output" )
type PromptSource ¶
type PromptSource struct {
ID PromptSourceID
Name string
Path string
}
type PromptSourceDescriptor ¶
type PromptSourceDescriptor struct {
ID PromptSourceID
Owner string
Description string
Allowed []PromptPlacement
StableByDefault bool
}
type PromptSourceID ¶
type PromptSourceID string
const ( PromptSourceKernel PromptSourceID = "runtime.kernel" PromptSourceHierarchy PromptSourceID = "runtime.hierarchy" PromptSourceWorkspace PromptSourceID = "workspace.definition" PromptSourceRuntime PromptSourceID = "runtime.context" PromptSourceSummary PromptSourceID = "context.summary" PromptSourceMemory PromptSourceID = "memory:workspace" PromptSourceSkillCatalog PromptSourceID = "skill:index" PromptSourceActiveSkills PromptSourceID = "skill:active" PromptSourceAgentDiscovery PromptSourceID = "agent:discovery" PromptSourceToolRegistry PromptSourceID = "tool_registry:native" // PromptSourceModuleKnowledge attributes a selected module's overlay and // skills, which are module-authored and enter context only on selection. PromptSourceModuleKnowledge PromptSourceID = "module:knowledge" PromptSourceToolDiscovery PromptSourceID = "tool_registry:discovery" PromptSourceOutputPolicy PromptSourceID = "runtime.output" PromptSourceSubTurnProfile PromptSourceID = "subturn.profile" PromptSourceUserMessage PromptSourceID = "turn:user_message" PromptSourceSteering PromptSourceID = "turn:steering" PromptSourceSubTurnResult PromptSourceID = "turn:subturn_result" PromptSourceToolResult PromptSourceID = "turn:tool_result" )
type PromptStack ¶
type PromptStack struct {
// contains filtered or unexported fields
}
func NewPromptStack ¶
func NewPromptStack(registry *PromptRegistry) *PromptStack
func (*PromptStack) Add ¶
func (s *PromptStack) Add(part PromptPart) error
func (*PromptStack) Parts ¶
func (s *PromptStack) Parts() []PromptPart
func (*PromptStack) Seal ¶
func (s *PromptStack) Seal()
type RuntimeEventObserver ¶
type RuntimeEventObserver interface {
OnRuntimeEvent(ctx context.Context, evt runtimeevents.Event) error
}
type SessionSummarizePayload ¶
type SessionSummarizePayload struct {
SummarizedMessages int
KeptMessages int
SummaryLen int
OmittedOversized bool
}
SessionSummarizePayload describes a completed async session summarization.
type SkillContextSnapshot ¶
type SoulDefinition ¶
SoulDefinition represents the resolved SOUL.md file linked to the agent.
type SteeringInjectedPayload ¶
SteeringInjectedPayload describes steering messages appended before the next LLM call.
type SteeringMode ¶
type SteeringMode string
SteeringMode controls how queued steering messages are dequeued.
const ( // SteeringOneAtATime dequeues only the first queued message per poll. SteeringOneAtATime SteeringMode = "one-at-a-time" // SteeringAll drains the entire queue in a single poll. SteeringAll SteeringMode = "all" // MaxQueueSize number of possible messages in the Steering Queue MaxQueueSize = 10 )
type SubTurnConfig ¶
type SubTurnConfig struct {
Tools []tools.Tool
SystemPrompt string
MaxTokens int
// Async controls the result delivery mechanism:
//
// When Async = false (synchronous sub-turn):
// - The caller blocks until the sub-turn completes
// - The result is ONLY returned via the function return value
// - The result is NOT delivered to the parent's pendingResults channel
// - This prevents double delivery: caller gets result immediately, no need for channel
// - Use case: When the caller needs the result immediately to continue execution
// - Example: A tool that needs to process the sub-turn result before returning
//
// When Async = true (asynchronous sub-turn):
// - The sub-turn runs in the background (still blocks the caller, but semantically async)
// - The result is delivered to the parent's pendingResults channel
// - The result is ALSO returned via the function return value (for consistency)
// - The parent turn can poll pendingResults in later iterations to process results
// - Use case: Fire-and-forget operations, or when results are processed in batches
// - Example: Spawning multiple sub-turns in parallel and collecting results later
//
// IMPORTANT: The Async flag does NOT make the call non-blocking. It only controls
// whether the result is delivered via the channel. For true non-blocking execution,
// the caller must spawn the sub-turn in a separate goroutine.
Async bool
// Critical indicates this SubTurn's result is important and should continue
// running even after the parent turn finishes gracefully.
//
// When parent finishes gracefully (Finish(false)):
// - Critical=true: SubTurn continues running, delivers result as orphan
// - Critical=false: SubTurn exits gracefully without error
//
// When parent finishes with hard abort (Finish(true)):
// - All SubTurns are canceled regardless of Critical flag
Critical bool
// Timeout is the maximum duration for this SubTurn.
// If the SubTurn runs longer than this, it will be canceled.
// Default is 5 minutes (defaultSubTurnTimeout) if not specified.
Timeout time.Duration
// MaxContextRunes limits the context size (in runes) passed to the SubTurn.
// This prevents context window overflow by truncating message history before LLM calls.
//
// Values:
// 0 = Auto-calculate based on model's ContextWindow * 0.75 (default, recommended)
// -1 = No limit (disable soft truncation, rely only on hard context errors)
// >0 = Use specified rune limit
//
// The soft limit acts as a first line of defense before hitting the provider's
// hard context window limit. When exceeded, older messages are intelligently
// truncated while preserving system messages and recent context.
MaxContextRunes int
// ActualSystemPrompt is injected as the true 'system' role message for the childAgent.
// SystemPrompt, despite its name, carries the task description and is sent as
// the first 'user' message.
ActualSystemPrompt string
// InitialMessages preloads the ephemeral session history before the agent loop starts.
// Used by evaluator-optimizer patterns to pass the full worker context across multiple iterations.
InitialMessages []providers.Message
// InitialTokenBudget is a shared atomic counter for tracking remaining tokens.
// If set, the SubTurn will inherit this budget and deduct tokens after each LLM call.
// If nil, the SubTurn will inherit the parent's tokenBudget (if any).
// Used by team tool to enforce token limits across all team members.
InitialTokenBudget *atomic.Int64
// TargetAgentID, when set, runs the sub-turn as the specified agent.
// The target agent's workspace, model, tools, and system prompt are used
// instead of the caller's. If empty, the sub-turn runs as the parent agent.
TargetAgentID string
}
SubTurnConfig configures the execution of a child sub-turn. The child runs on its agent's model — or, running as its parent's agent, on the model the parent's message selected.
Usage Examples:
Synchronous sub-turn (Async=false):
cfg := SubTurnConfig{
SystemPrompt: "Analyze this code",
Async: false, // Result returned immediately
}
result, err := SpawnSubTurn(ctx, cfg)
// Use result directly here
processResult(result)
Asynchronous sub-turn (Async=true):
cfg := SubTurnConfig{
SystemPrompt: "Background analysis",
Async: true, // Result delivered to channel
}
result, err := SpawnSubTurn(ctx, cfg)
// Result also available in parent's pendingResults channel
// Parent turn will poll and process it in a later iteration
type SubTurnEndPayload ¶
SubTurnEndPayload describes the completion of a child turn.
type SubTurnOrphanPayload ¶
SubTurnOrphanPayload describes a sub-turn result that could not be delivered.
type SubTurnResultDeliveredPayload ¶
type SubTurnResultDeliveredPayload struct {
TargetChannel string
TargetChatID string
ContentLen int
}
SubTurnResultDeliveredPayload describes delivery of a sub-turn result.
type SubTurnSpawnPayload ¶
SubTurnSpawnPayload describes the creation of a child turn.
type ThinkingLevel ¶
type ThinkingLevel string
ThinkingLevel controls how the provider sends thinking parameters.
- "adaptive": sends {thinking: {type: "adaptive"}} + output_config.effort (Claude 4.6+)
- "low"/"medium"/"high"/"xhigh": sends {thinking: {type: "enabled", budget_tokens: N}} (all models)
- "off": disables thinking
const ( ThinkingOff ThinkingLevel = "off" ThinkingLow ThinkingLevel = "low" ThinkingMedium ThinkingLevel = "medium" ThinkingHigh ThinkingLevel = "high" ThinkingXHigh ThinkingLevel = "xhigh" ThinkingAdaptive ThinkingLevel = "adaptive" )
type Tool ¶
Tool is the kernel's public tool contract, re-exported so an embedder does not have to reach into pkg/tools/shared to satisfy ToolProvider.
A four-method interface (Name, Description, Parameters, Execute) and nothing more. Keeping the embedding surface this small is what lets a standalone product bind its own capabilities natively.
type ToolApprovalRequest ¶
type ToolApprovalRequest struct {
Meta HookMeta `json:"meta"`
Context *TurnContext `json:"context,omitempty"`
Tool string `json:"tool"`
Arguments map[string]any `json:"arguments,omitempty"`
}
func (*ToolApprovalRequest) Clone ¶
func (r *ToolApprovalRequest) Clone() *ToolApprovalRequest
type ToolApprover ¶
type ToolApprover interface {
ApproveTool(ctx context.Context, req *ToolApprovalRequest) (ApprovalDecision, error)
}
type ToolCallHookRequest ¶
type ToolCallHookRequest struct {
Meta HookMeta `json:"meta"`
Context *TurnContext `json:"context,omitempty"`
Tool string `json:"tool"`
Arguments map[string]any `json:"arguments,omitempty"`
Channel string `json:"channel,omitempty"`
ChatID string `json:"chat_id,omitempty"`
HookResult *tools.ToolResult `json:"hook_result,omitempty"` // Result returned directly by hook (for respond action). Media is supported - see Media handling section in docs.
}
func (*ToolCallHookRequest) Clone ¶
func (r *ToolCallHookRequest) Clone() *ToolCallHookRequest
type ToolControl ¶
type ToolControl int
ToolControl signals returned from ExecuteTools to drive tool loop iteration.
const ( // ToolControlContinue tells the tool loop to jump to the next iteration // (pendingMessages arrived, SubTurn results, etc.). ToolControlContinue ToolControl = iota // ToolControlBreak tells the tool loop to exit and return to the coordinator. ToolControlBreak // ToolControlFinalize tells the coordinator that all tool responses were // handled and the turn should finalize without another LLM call. ToolControlFinalize )
type ToolExecEndPayload ¶
type ToolExecEndPayload struct {
Tool string
Duration time.Duration
ForLLMLen int
ForUserLen int
IsError bool
Async bool
}
ToolExecEndPayload describes the outcome of a tool execution.
type ToolExecSkippedPayload ¶
ToolExecSkippedPayload describes a skipped tool call.
type ToolExecStartPayload ¶
ToolExecStartPayload describes a tool execution request.
type ToolExecutionRecord ¶
type ToolInterceptor ¶
type ToolInterceptor interface {
BeforeTool(ctx context.Context, call *ToolCallHookRequest) (*ToolCallHookRequest, HookDecision, error)
AfterTool(ctx context.Context, result *ToolResultHookResponse) (*ToolResultHookResponse, HookDecision, error)
}
type ToolProvider ¶
type ToolProvider interface {
// RegisterTools contributes tools via register and returns one-line
// summaries for the agent's context.
//
// workspace is the AGENT'S OWN working directory, and it is passed rather
// than captured because it is resolved PER AGENT inside the registration
// loop -- the composition root does not know it. An earlier version of this
// interface omitted it, which only surfaced when wiring the call sites:
// the provider would have had to invent a workspace, and every agent would
// have shared one.
//
// The returned knowledge function is optional -- nil is valid, and means
// this provider has no per-selection knowledge to load. It maps a
// provider-defined identifier to overlays, skills and warnings.
RegisterTools(workspace string, register func(Tool)) (summaries []string,
knowledge func(id string) (overlays string, skills string, warnings []string))
}
ToolProvider contributes tools to an agent from outside the kernel.
THIS IS THE KERNEL'S ONLY DOOR FOR NON-GENERIC CAPABILITIES, and it exists so the kernel can stay Layer 1.
The kernel previously imported internal/moduletools directly and called it from registerSharedTools. That was a Layer 1 -> Layer 2 inversion, disqualifying twice over: architecturally a standalone product embedding the kernel dragged in module discovery, the v2 contract gate and bounded subprocess execution it would never use; and mechanically internal/ packages CANNOT BE IMPORTED by an external module, so any sibling embedding the kernel would simply fail to compile.
That one import pulled in six Layer-2/3 packages transitively -- the module host, subprocess execution, both wire protocols, the contract gate, and the cockpit's artefact view vocabulary. Removing it drops all six at once; the audit found the other 55 of 56 transitive packages already generic.
WHAT CROSSES THE BOUNDARY IS DELIBERATELY PLAIN. Tools arrive as tools.Tool, a four-method interface; the descriptive lines are []string; the knowledge loader is a plain func. No provider type reaches the agent struct, which is what makes the kernel independently consumable rather than merely separately compiled.
Three shapes this serves:
full Compa -- supplies a provider backed by the detached-module host
standalone -- supplies a NATIVE, in-process provider binding its own
capabilities, paying no subprocess or protocol cost to
host itself
bare kernel -- supplies none, and is a plain conversational runtime
type ToolResultHookResponse ¶
type ToolResultHookResponse struct {
Meta HookMeta `json:"meta"`
Context *TurnContext `json:"context,omitempty"`
Tool string `json:"tool"`
Arguments map[string]any `json:"arguments,omitempty"`
Result *tools.ToolResult `json:"result,omitempty"`
Duration time.Duration `json:"duration"`
}
func (*ToolResultHookResponse) Clone ¶
func (r *ToolResultHookResponse) Clone() *ToolResultHookResponse
type TurnContext ¶
type TurnContext struct {
Inbound *bus.InboundContext `json:"inbound,omitempty"`
Route *routing.ResolvedRoute `json:"route,omitempty"`
Scope *session.SessionScope `json:"scope,omitempty"`
}
TurnContext carries normalized turn-scoped facts that can be shared across events, hooks, and other runtime observers without re-parsing raw message fields.
type TurnEndPayload ¶
type TurnEndPayload struct {
Status TurnEndStatus
Workspace string
Iterations int
Duration time.Duration
FinalContentLen int
UserMessage string
FinalContent string
ActiveSkills []string
AttemptedSkills []string
FinalSuccessfulPath []string
SkillContextSnapshots []SkillContextSnapshot
ToolKinds []string
ToolExecutions []ToolExecutionRecord
// Error says why a turn that ended with TurnEndStatusError failed.
Error string
}
TurnEndPayload describes the completion of a turn.
type TurnEndStatus ¶
type TurnEndStatus string
TurnEndStatus describes the terminal state of a turn.
const ( // TurnEndStatusCompleted indicates the turn finished normally. TurnEndStatusCompleted TurnEndStatus = "completed" // TurnEndStatusError indicates the turn ended because of an error. TurnEndStatusError TurnEndStatus = "error" // TurnEndStatusAborted indicates the turn was hard-aborted and rolled back. TurnEndStatusAborted TurnEndStatus = "aborted" )
type TurnStartPayload ¶
TurnStartPayload describes the start of a turn.
type UserDefinition ¶
UserDefinition represents the resolved USER.md file linked to the workspace.
Source Files
¶
- agent.go
- agent_command.go
- agent_event.go
- agent_init.go
- agent_inject.go
- agent_mcp.go
- agent_media.go
- agent_message.go
- agent_models.go
- agent_options.go
- agent_outbound.go
- agent_steering.go
- agent_stop.go
- agent_transcribe.go
- agent_utils.go
- context.go
- context_budget.go
- context_default.go
- context_manager.go
- context_seahorse.go
- context_usage.go
- definition.go
- discovery.go
- dispatch_request.go
- error_format.go
- event_payloads.go
- events.go
- events_runtime.go
- evolution_bridge.go
- hook_mount.go
- hook_process.go
- hooks.go
- instance.go
- instance_selection.go
- instance_selection_metadata.go
- llm_media.go
- memory.go
- model_resolution.go
- pipeline.go
- pipeline_execute.go
- pipeline_finalize.go
- pipeline_llm.go
- pipeline_setup.go
- pipeline_streaming.go
- prompt.go
- prompt_contributors.go
- prompt_turn.go
- registry.go
- runtime_event_logger.go
- steering.go
- subturn.go
- thinking.go
- tool_allowlist.go
- turn_context.go
- turn_coord.go
- turn_profile_policy.go
- turn_state.go