Documentation
¶
Overview ¶
Package agentruntime provides per-agent runtime instances for team mode (RMI-OMNIAGENT-309). A chat names an agent; each turn must run on an instance built from that agent's persona + enabled skills (and, once RMI-310 lands, its agent-scoped secrets). Building an instance is expensive — it opens an LLM client and loads skills — so instances are built lazily on first use and held in a bounded LRU cache: a busy deployment keeps only its hottest agents resident, and an idle agent is evicted rather than pinned in memory forever.
Cache satisfies the chats.AgentRuntime seam (Slug + Processor), so the chats service routes an agent-bound chat's turns to the agent's own instance. The cache itself depends only on two seams — a ConfigLoader (reads an agent's runtime configuration by ID, in system context) and a Builder (turns that configuration into a processor) — so it is independent of the LLM/skill stack and unit-testable with fakes. AgentBuilder (builder.go) is the production Builder that constructs a real *agent.Agent.
Index ¶
Constants ¶
const DefaultMaxInstances = 64
DefaultMaxInstances bounds how many per-agent instances the cache holds resident before evicting the least-recently-used one. TRD §9 Q3 leaves the eviction policy to be tuned in Phase 4; this is a conservative default.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type AgentBuilder ¶
type AgentBuilder struct {
// contains filtered or unexported fields
}
AgentBuilder is the production Builder: it constructs a real *agent.Agent from an agent's persona + enabled skills + agent-scoped secrets. The persona becomes the system prompt; the enabled-skill subset is applied via WithSkillIncludes over the shared BaseOptions skill source; model/provider fall back to the deployment defaults; and, when a SecretSource is configured, the agent's secrets are resolved and injected via WithSecretEnv (per-agent MCP subprocess env and other secrets-aware skills), so two agents load disjoint secrets with no cross-leak (RMI-OMNIAGENT-310).
func NewAgentBuilder ¶
func NewAgentBuilder(cfg BuilderConfig) *AgentBuilder
NewAgentBuilder creates the production builder.
func (*AgentBuilder) Build ¶
func (b *AgentBuilder) Build(ctx context.Context, cfg AgentConfig) (chats.AgentProcessor, error)
Build constructs the agent instance for cfg. It satisfies Builder.
type AgentConfig ¶
type AgentConfig struct {
ID uuid.UUID
Slug string
Name string
Persona string
Model string
Provider string
Skills []string
}
AgentConfig is an agent's resolved runtime configuration, loaded by ID. The runtime is a system principal (not a user), so a ConfigLoader reads it in system context, independent of RLS visibility.
type Builder ¶
type Builder interface {
Build(ctx context.Context, cfg AgentConfig) (chats.AgentProcessor, error)
}
Builder turns a resolved AgentConfig into a processor. It is the injection point for the LLM/skill stack (and, in RMI-310, agent-scoped secret binding), keeping the cache independent of the agent package and unit-testable. A processor that holds resources (e.g. *agent.Agent) should implement io.Closer; the cache closes it on eviction and on Close.
type BuilderConfig ¶
type BuilderConfig struct {
// Defaults supplies the deployment-wide LLM configuration (provider, model,
// API key, base URL, timezone, temperature/token limits). An agent's own
// Model/Provider override the defaults when set; everything else is inherited.
Defaults agent.Config
// BaseOptions are agent.Options shared by every built instance — the
// deployment's skill source (skill pack/manager or dirs), tools, session
// store, and rollover policy. The builder layers each agent's enabled-skill
// subset (WithSkillIncludes) on top, so BaseOptions supplies where skills
// come from and the agent's config selects which are active.
BaseOptions []agent.Option
// Secrets resolves each agent's agent-scoped secrets, injected into the
// instance's secrets-aware skills (notably per-agent MCP subprocess env) via
// agent.WithSecretEnv. Optional; nil means no secrets are injected. Because
// each instance is built with only its own agent's secrets, two agents load
// disjoint secrets with no cross-leak (RMI-OMNIAGENT-310).
Secrets SecretSource
// Logger defaults to slog.Default().
Logger *slog.Logger
}
BuilderConfig configures the production AgentBuilder.
type Cache ¶
type Cache struct {
// contains filtered or unexported fields
}
Cache is a lazy, bounded-LRU cache of per-agent runtime instances. It satisfies chats.AgentRuntime. Safe for concurrent use.
func (*Cache) Close ¶
Close evicts and closes every resident instance. The cache is reusable afterward (a later Processor call rebuilds).
func (*Cache) Invalidate ¶
Invalidate evicts an agent's cached instance (if any) and closes it, so the next turn rebuilds from current configuration. Call it when an agent's persona/skills change. It is a no-op when the agent is not resident.
func (*Cache) Processor ¶
Processor returns the agent's instance, building it on first use and caching it (evicting the least-recently-used instance when over capacity). Concurrent callers for the same agent share a single build. A build error is not cached: the poisoned entry is dropped so the next call retries.
type Config ¶
type Config struct {
// Loader reads agents' runtime configuration. Required.
Loader ConfigLoader
// Builder builds an instance from a config. Required.
Builder Builder
// MaxInstances bounds the resident instance count; <=0 uses
// DefaultMaxInstances.
MaxInstances int
// Logger defaults to slog.Default().
Logger *slog.Logger
}
Config configures the cache.
type ConfigLoader ¶
type ConfigLoader interface {
// AgentSlug returns just the agent's slug — the cheap read a group turn
// makes for @-mention matching, without building the instance.
AgentSlug(ctx context.Context, agentID uuid.UUID) (string, error)
// LoadConfig returns the agent's full runtime configuration.
LoadConfig(ctx context.Context, agentID uuid.UUID) (AgentConfig, error)
}
ConfigLoader reads an agent's runtime configuration by ID, in system context. The two methods are split so a group turn can make the cheap @-mention check (AgentSlug) without triggering a full config load or instance build.
type SecretSource ¶
type SecretSource interface {
ResolveSecrets(ctx context.Context, agentID uuid.UUID, skillNames []string) (map[string]string, error)
}
SecretSource resolves an agent's secrets, by ID, into the environment map its runtime instance injects (env-var name → value). It is the seam through which agent-scoped secrets (RMI-OMNIAGENT-310) reach the builder, keeping the builder independent of the secret store and unit-testable with a fake. A nil SecretSource means no secrets are injected (prior behavior). Resolution runs in system context — the runtime is a system principal, not a user.
skillNames is the agent's own enabled-skill list (AgentConfig.Skills) — an implementation that layers in skill-scoped fallback bindings (RMI-OMNIAGENT-208) must restrict them to these names, never the deployment's full skill set, or it leaks a binding into an agent that doesn't have that skill enabled.