agent

package
v0.219.0 Latest Latest
Warning

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

Go to latest
Published: Sep 30, 2026 License: Apache-2.0 Imports: 34 Imported by: 0

README

agent

What - the agent bounded context: the event-driven state machine that turns a user turn into model calls, tool calls and approvals. Why - the loop is the core domain of the CLI, and every other context exists to hand it tools, storage or transport. How - agent_event_driven.go routes events to states/, which runs one executor per state. agent_state_machine.go defines the allowed transitions, and domain/ holds the ports the loop calls.

How it plugs in

  • domain/ is the shared kernel (tool contracts and results, agent mode, chat events) plus the service ports the loop calls: skills, GitHub, command hooks, system reminders, user questions, media and image annotation.
  • The capabilities implement those ports: skills the skills service, github the issue and setup services, plugins a command-hook provider.
  • The loop is a conformist to the Inference Gateway SDK: it uses the SDK's message and tool-call types directly, so an SDK change reaches the loop without a translation layer.
  • infrastructure/ holds the adapters for the file, frame-source and media ports. The media adapters call the gateway.
  • internal/container/container.go builds the agent and wires every port.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func InjectDueReminders added in v0.163.0

func InjectDueReminders(provider agentdomain.SystemReminderProvider, q agentdomain.ReminderQuery, deliver func(agentdomain.SystemReminder))

InjectDueReminders is the single reminder-injection seam shared by the chat (Agent) and headless (AgentSession) loops: it resolves the reminders due for q, delivers each via the caller-owned deliver callback (the two loops hold different conversation representations), logs it, emits the tagged system_reminder stream event, and marks the name in q.Fired. Callers own provider resolution, the awaiting-tool-results guard, query construction, and locking.

func NewAgentStateMachine

func NewAgentStateMachine() states.AgentStateMachine

NewAgentStateMachine creates a new agent state machine

func RunCommandHooks added in v0.125.0

func RunCommandHooks(ctx context.Context, cfg *config.Config, provider agentdomain.HookCommandProvider, mode agentdomain.AgentMode, hook agentdomain.HookPoint, turn int, sessionID string)

RunCommandHooks is the single chokepoint both agents use to run command hooks. It asks the provider which commands are attached to hook, gates each on the per-mode bash allow-list - the SAME matcher a model-proposed bash command faces, so command hooks open no new bypass of the secure-by-default model - and runs the allowed ones fire-and-observe. Off-list commands are skipped and reported with the rejection hint (the user authorizes a command by allow-listing it, e.g. tools.bash.mode.*.allow or INFER_TOOLS_BASH_ALLOW_APPEND).

Both the event-driven chat agent and the headless `infer headless` loop call this from their dispatchHooks seam so the gate and observability cannot drift apart. cfg supplies the allow-list and the fallback provider; mode selects the per-mode allow-list; sessionID and turn populate the command's stdin JSON context. Commands run synchronously; their output is emitted as a hook_command stream event and logged, never fed back into the conversation or used to alter the loop (that feedback is a later iteration).

Types

type Agent added in v0.207.0

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

Agent implements the AgentService interface with direct chat functionality

func NewAgent

func NewAgent(
	client sdk.Client,
	toolService agentdomain.ToolService,
	cfg *config.Config,
	conversationRepo convdomain.ConversationRepository,
	a2aAgents func() string,
	skillsService agentdomain.SkillsService,
	messageQueue convdomain.MessageQueue,
	stateManager stateStore,
	timeoutSeconds int,
	optimizer convdomain.ConversationOptimizer,
	bgRegistry scheddomain.BackgroundTaskRegistry,
	rolloverManager convdomain.SessionRollover,
	tokenizer usageEstimator,
	hookProvider agentdomain.HookCommandProvider,
	pluginInstructions func() string,
) *Agent

func (*Agent) BuildSystemPrompt added in v0.207.0

func (s *Agent) BuildSystemPrompt() string

BuildSystemPrompt assembles the static system prompt sent as message[0] (base prompt + custom instructions + AGENTS.md + plugins + static context). It is deliberately byte-identical across turns and agent-mode switches so LLM servers get KV-cache prefix hits; volatile context rides in volatileTailMessage and per-mode behaviour in the mode-change reminder. Returns "" when unset.

func (*Agent) CancelRequest added in v0.207.0

func (s *Agent) CancelRequest(requestID string) error

CancelRequest cancels an active request. Safe to call repeatedly - no-ops via sync.Once on the underlying sessionCancel - and returns nil even for an unknown requestID, so the UI can fire it on every Esc press without spurious errors after teardown. The agent loop publishes ChatCompleteEvent{Cancelled:true} as the single cancel-completion signal.

func (*Agent) GetMetrics added in v0.207.0

func (s *Agent) GetMetrics(requestID string) *agentdomain.ChatMetrics

GetMetrics returns metrics for a completed request

func (*Agent) GetReasoningEffort added in v0.207.0

func (s *Agent) GetReasoningEffort() string

GetReasoningEffort returns the effort level currently applied to requests ("" = provider default).

func (*Agent) Run added in v0.207.0

func (*Agent) RunStreaming added in v0.207.0

func (s *Agent) RunStreaming(
	ctx context.Context,
	req *agentdomain.AgentRequest,
	onDelta func(content, reasoning string, toolCalls []sdk.ChatCompletionMessageToolCallChunk),
) (*agentdomain.ChatSyncResponse, error)

RunStreaming executes a single model turn with streaming, invoking onDelta for each content/reasoning/tool-call delta as it arrives, and returns the same assembled ChatSyncResponse as Run: the streaming counterpart for callers that own their own agentic loop (the headless AG-UI agent). onDelta may be nil.

func (*Agent) RunWithStream added in v0.207.0

func (s *Agent) RunWithStream(ctx context.Context, req *agentdomain.AgentRequest) (<-chan agentdomain.ChatEvent, error)

RunWithStream executes an agent task with streaming (for interactive chat)

func (*Agent) SetCurrentModelFn added in v0.207.0

func (s *Agent) SetCurrentModelFn(fn func() string)

SetCurrentModelFn wires the session's current-model accessor so the judge follows a model picked at runtime, not only the configured agent.model.

func (*Agent) SetMemoryBackend added in v0.207.0

func (s *Agent) SetMemoryBackend(backend memory.MemoryBackend)

SetMemoryBackend wires the memory sync backend so the chat agent pulls memory once at session start (SyncIn on HookPreSession). SyncOut is driven by the Memory tool on write/delete, not here - chat fires HookPostSession after every message, so pushing there would commit-storm. A nil backend disables sync.

func (*Agent) SetReasoningEffort added in v0.207.0

func (s *Agent) SetReasoningEffort(effort string) error

SetReasoningEffort updates the reasoning effort applied to subsequent requests. An empty string resets to the provider default.

func (*Agent) SetTelemetryRecorder added in v0.207.0

func (s *Agent) SetTelemetryRecorder(rec *telemetry.Recorder)

SetTelemetryRecorder wires the telemetry recorder so per-request token usage is tapped in storeIterationMetrics. A nil recorder disables recording.

func (*Agent) SystemPromptSections added in v0.207.0

func (s *Agent) SystemPromptSections() []PromptSection

SystemPromptSections returns the labeled parts of the prompt context a fresh session (turn 0) would send — the static system prompt sections followed by the Volatile-marked tail sections — with empty parts omitted. Exposed for the `infer debug agent system_prompt --tokens` breakdown.

func (*Agent) VolatileTailText added in v0.207.0

func (s *Agent) VolatileTailText() (string, bool)

VolatileTailText renders the volatile-context tail a fresh session (turn 0) would send as a hidden per-request <system-reminder> user message; ok=false means no tail is sent. Exposed for the `infer debug agent system_prompt` command via type assertion, alongside SystemPromptSections.

type ApprovalTools added in v0.216.0

type ApprovalTools interface {
	agentdomain.ToolManifestLookup
	GetTool(name string) (agentdomain.Tool, error)
}

ApprovalTools is what the approval policy needs to know about the tools: a tool's manifest, and the tool itself when it approves per call.

type EventDrivenAgent

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

EventDrivenAgent manages agent execution using event-driven state machine

func NewEventDrivenAgent

func NewEventDrivenAgent(
	service *Agent,
	cfg *config.AgentConfig,
	ctx context.Context,
	req *agentdomain.AgentRequest,
	conversation *[]sdk.Message,
	eventPublisher *eventPublisher,
	cancelChan <-chan struct{},
	provider string,
	model string,
	registry scheddomain.BackgroundTaskRegistry,
) *EventDrivenAgent

NewEventDrivenAgent creates a new event-driven agent

func (*EventDrivenAgent) Start

func (a *EventDrivenAgent) Start()

Start begins the event-driven agent execution. The state machine already begins in Idle, so seeding a MessageReceivedEvent drives the first transition (Idle -> CheckingQueue) via the Idle state handler.

func (*EventDrivenAgent) Wait

func (a *EventDrivenAgent) Wait()

Wait waits for the agent to complete

type IndexedToolResult

type IndexedToolResult struct {
	Index  int
	Result convdomain.ConversationEntry
}

type JudgeApprover added in v0.207.0

type JudgeApprover interface {
	Judge(ctx context.Context, in JudgeInput) (JudgeVerdict, error)
}

JudgeApprover decides one pending tool call by asking a small LLM whether it serves the user's intent and is safe. The judge is the approver selected by approval_behaviour "judge" / agent mode auto-with-judge: it is always reachable, so headless and CI get a real approver instead of blocking.

type JudgeInput added in v0.207.0

type JudgeInput struct {
	Model      string
	RootIntent string
	Intent     string
	Action     string
}

JudgeInput is one approval question: Model is the resolved "provider/model" id that answers it, RootIntent the first non-hidden user message of the session, Intent the latest one, and Action the pending tool call (name + arguments).

type JudgeVerdict added in v0.207.0

type JudgeVerdict struct {
	Decision agentdomain.JudgeDecision
	Reason   string
	Usage    *sdk.CompletionUsage `json:"-"`
}

JudgeVerdict is the parsed decision of the LLM judge for one pending tool call. Decision is always one of the agentdomain.JudgeDecision values once parsed; nothing downstream reads the judge's raw output.

func ParseJudgeVerdict added in v0.207.0

func ParseJudgeVerdict(raw string) (JudgeVerdict, error)

ParseJudgeVerdict extracts the verdict JSON object from the judge's raw output. Keeping only the outermost {...} drops code fences and prose; it requires decision to be one of the two literals, and rejects anything else so a malformed judge response flows into on_error handling.

func (JudgeVerdict) Approved added in v0.207.0

func (v JudgeVerdict) Approved() bool

Approved reports whether the judge approved the action.

type LLMJudge added in v0.188.0

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

LLMJudge implements JudgeApprover with one no-tools GenerateContent call (same shape as GenerateLLMSummary: direct client call, SkipMCP, its own timeout, bounded max_tokens). The judge.yaml on_error policy decides what a failed call means: deny (default) fails closed with a distinguishable reason - the same default as the no-approver block path - allow approves.

func NewLLMJudge added in v0.188.0

func NewLLMJudge(client sdk.Client, cfg *config.Config) *LLMJudge

NewLLMJudge creates the judge approver. Config validation fails fast at startup when no model is resolvable, so an empty model here is a hard error. judge.gateway_url gives the judge its own client (same API key and timeout as the agent's) so it can answer from a different gateway than the driver.

func (*LLMJudge) Judge added in v0.188.0

func (j *LLMJudge) Judge(ctx context.Context, in JudgeInput) (JudgeVerdict, error)

Judge decides one pending tool call: does it serve the user's intent and is it safe to run? The verdict contract is enforced by ParseJudgeVerdict.

type LLMToolService added in v0.178.1

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

LLMToolService implements ToolService with the new tools package architecture

func NewLLMToolServiceWithRegistry added in v0.178.1

func NewLLMToolServiceWithRegistry(cfg *config.Config, registry *tools.Registry) *LLMToolService

NewLLMToolServiceWithRegistry creates a new LLM tool service with an existing registry

func (*LLMToolService) ExecuteTool added in v0.178.1

ExecuteTool executes a tool with the given arguments

func (*LLMToolService) ExecuteToolDirect added in v0.178.1

ExecuteToolDirect executes a tool directly without checking if it's enabled Used for user-initiated commands where the user explicitly wants to run the tool

func (*LLMToolService) GetTool added in v0.178.1

func (s *LLMToolService) GetTool(name string) (agentdomain.Tool, error)

func (*LLMToolService) IsToolEnabled added in v0.178.1

func (s *LLMToolService) IsToolEnabled(name string) bool

IsToolEnabled checks if a tool is enabled

func (*LLMToolService) ListAvailableTools added in v0.178.1

func (s *LLMToolService) ListAvailableTools() []string

ListAvailableTools returns names of all enabled tools

func (*LLMToolService) ListMarkdownSubagents added in v0.214.0

func (s *LLMToolService) ListMarkdownSubagents() []agentdomain.SubagentInfo

ListMarkdownSubagents returns the Markdown-defined subagent presets loaded this session (.infer/agents/*.md and ~/.infer/agents/*.md), for /agents.

func (*LLMToolService) ListTools added in v0.178.1

func (s *LLMToolService) ListTools() []sdk.ChatCompletionTool

ListTools returns definitions for all enabled tools

func (*LLMToolService) ListToolsForMode added in v0.178.1

func (s *LLMToolService) ListToolsForMode(mode agentdomain.AgentMode) []sdk.ChatCompletionTool

ListToolsForMode returns definitions for enabled tools filtered by agent mode

func (*LLMToolService) Manifest added in v0.216.0

func (s *LLMToolService) Manifest(name string) agentdomain.ToolManifest

Manifest returns the named tool's manifest, whichever bounded context defines the tool.

func (*LLMToolService) ValidateTool added in v0.178.1

func (s *LLMToolService) ValidateTool(name string, args map[string]any) error

ValidateTool validates tool arguments

type NoOpToolService added in v0.178.1

type NoOpToolService struct{}

NoOpToolService implements ToolService as a no-op (when tools are disabled)

func NewNoOpToolService added in v0.178.1

func NewNoOpToolService() *NoOpToolService

NewNoOpToolService creates a new no-op tool service

func (*NoOpToolService) ExecuteTool added in v0.178.1

func (*NoOpToolService) ExecuteToolDirect added in v0.178.1

func (*NoOpToolService) GetTool added in v0.178.1

func (s *NoOpToolService) GetTool(name string) (agentdomain.Tool, error)

func (*NoOpToolService) IsToolEnabled added in v0.178.1

func (s *NoOpToolService) IsToolEnabled(name string) bool

func (*NoOpToolService) ListAvailableTools added in v0.178.1

func (s *NoOpToolService) ListAvailableTools() []string

func (*NoOpToolService) ListMarkdownSubagents added in v0.214.0

func (s *NoOpToolService) ListMarkdownSubagents() []agentdomain.SubagentInfo

func (*NoOpToolService) ListTools added in v0.178.1

func (s *NoOpToolService) ListTools() []sdk.ChatCompletionTool

func (*NoOpToolService) ListToolsForMode added in v0.178.1

func (s *NoOpToolService) ListToolsForMode(mode agentdomain.AgentMode) []sdk.ChatCompletionTool

func (*NoOpToolService) Manifest added in v0.216.0

func (s *NoOpToolService) Manifest(name string) agentdomain.ToolManifest

func (*NoOpToolService) ValidateTool added in v0.178.1

func (s *NoOpToolService) ValidateTool(name string, args map[string]any) error

type PromptSection added in v0.140.0

type PromptSection struct {
	Name     string
	Text     string
	Volatile bool
}

PromptSection is one labeled part of the assembled prompt context, exposed for diagnostics (e.g. per-section token estimates in `infer debug`). Text is the raw part as it appears in the prompt, including any separator prefix. Volatile marks sections delivered via the per-request tail message rather than the static system prompt.

type StandardApprovalPolicy added in v0.178.1

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

StandardApprovalPolicy implements the default approval policy with the following rules:

  1. A tool that approves per call (agentdomain.CallApprover) decides for itself, ahead of the agent mode
  2. Auto-accept mode bypasses all approval 2.5. ReadOnly mode (Explore-like subagent) bypasses approval; its toolset is read-only by construction so nothing it can call mutates. Neither needs a call the mode does not make available: execution rejects it
  3. Non-chat (headless agent) mode bypasses approval; there the Bash tool's own per-mode gate (executeBash) decides what runs
  4. Bash commands are governed by the per-mode allow-list (config.IsBashCommandAllowed): reached only in chat, non-auto mode, so allowed commands bypass approval and anything off-list prompts the user
  5. Other tools follow their own require_approval setting, then their manifest's default, then the global require_approval setting

func NewStandardApprovalPolicy added in v0.178.1

func NewStandardApprovalPolicy(cfg *config.Config, stateManager agentdomain.AgentModeState, tools ApprovalTools) *StandardApprovalPolicy

NewStandardApprovalPolicy creates a new standard approval policy. tools resolves each tool's manifest for its plan-mode and approval defaults; with nil every tool gets the default policy.

func (*StandardApprovalPolicy) ShouldRequireApproval added in v0.178.1

func (p *StandardApprovalPolicy) ShouldRequireApproval(
	ctx context.Context,
	toolCall *sdk.ChatCompletionMessageToolCall,
	isChatMode bool,
) bool

ShouldRequireApproval implements the approval decision logic

type StateMachine added in v0.207.0

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

StateMachine implements the AgentStateMachine interface.

The state machine manages the agent's execution flow through the following states:

State Flow:

Idle → CheckingQueue → StreamingLLM → PostStream → EvaluatingTools → ApprovingTools/BlockingTools/ExecutingTools → PostToolExecution → CheckingQueue (loop) → Completing → Idle

State Descriptions:

  • Idle: Agent is not executing, waiting for work
  • CheckingQueue: Checking if there are queued messages or if completion criteria are met
  • StreamingLLM: Streaming responses from the LLM
  • PostStream: Processing LLM response, checking for tool calls or completion
  • EvaluatingTools: Determining if tool calls need approval
  • ApprovingTools: Waiting for user approval of tool calls (only in chat mode)
  • BlockingTools: Approval required but undeliverable (approval_behaviour=block); gated tools are rejected with a reason
  • ExecutingTools: Executing approved or auto-approved tool calls
  • PostToolExecution: Processing tool results, checking for completion or continuing
  • Completing: Finalizing the agent execution
  • Error: An error occurred during execution
  • Cancelled: User cancelled the execution
  • Stopped: Tool execution indicated stop (user rejection or error)

Thread Safety:

All state transitions are protected by a read-write mutex to ensure thread-safe access.

func (*StateMachine) CanTransition added in v0.207.0

func (sm *StateMachine) CanTransition(ctx *states.AgentContext, targetState states.AgentExecutionState) bool

CanTransition checks if a transition from current state to target state is valid This is useful for checking before attempting a transition

func (*StateMachine) GetCurrentState added in v0.207.0

func (sm *StateMachine) GetCurrentState() states.AgentExecutionState

GetCurrentState returns the current state (thread-safe)

func (*StateMachine) GetPreviousState added in v0.207.0

func (sm *StateMachine) GetPreviousState() states.AgentExecutionState

GetPreviousState returns the previous state (thread-safe)

func (*StateMachine) GetValidTransitions added in v0.207.0

func (sm *StateMachine) GetValidTransitions(ctx *states.AgentContext) []states.AgentExecutionState

GetValidTransitions returns all valid transitions from the current state

func (*StateMachine) Reset added in v0.207.0

func (sm *StateMachine) Reset()

Reset resets the state machine to idle

func (*StateMachine) Transition added in v0.207.0

func (sm *StateMachine) Transition(ctx *states.AgentContext, targetState states.AgentExecutionState) error

Transition attempts to transition to the target state

type StateTransition

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

StateTransition represents a state transition with guard and action

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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