agent

package
v0.5.1 Latest Latest
Warning

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

Go to latest
Published: Aug 28, 2026 License: Apache-2.0 Imports: 13 Imported by: 0

Documentation

Overview

Package agent is the external control-plane layer that composes the gateway's LLM, MCP, ACP, and metrics surfaces around an operator-facing agent identity.

It depends on the lower-level protocol managers and query services; the protocol packages must not depend on pkg/agent. See docs/design/agents-control-plane.md for the full direction.

Index

Constants

View Source
const (
	TopologyKindSingle      = "single"
	TopologyKindSequential  = "sequential"
	TopologyKindParallel    = "parallel"
	TopologyKindLoop        = "loop"
	TopologyKindSupervisor  = "supervisor"
	TopologyKindPlanExecute = "planexecute"
	TopologyKindDeep        = "deep"
	// TopologyKindCustom selects a compiled-in agent factory by name
	// (§5.7.3); the factory must be registered in the linked binary.
	TopologyKindCustom = "custom"
)

Builtin topology kinds. They enumerate what eino ADK exposes as parameterizable structure (docs/design/agents-control-plane.md §5.7.2).

View Source
const (
	PermissionModeAutoApprove = "auto_approve"
	PermissionModeInteractive = "interactive"
)

Builtin tool-permission modes (§5.7.7). There is no "deny" mode: builtin tools are operator-declared allowlists already; a fully denied toolset is a definition without tools.

View Source
const (
	// RuntimeTypeACP: the gateway owns the lifecycle (process pool, sessions,
	// permission flow, transcript) for the Agent.
	RuntimeTypeACP = "acp"
	// RuntimeTypeHTTP: the agent service owns its own lifecycle; the gateway is
	// only a client. P0 defines the shape but does not dispatch to it yet.
	RuntimeTypeHTTP = "http"
	// RuntimeTypeBuiltin: no separate process at all — the agent is a persisted
	// definition materialized by the in-process generic ADK host
	// (docs/design/agents-control-plane.md §5.7).
	RuntimeTypeBuiltin = "builtin"
)

Runtime backend types, split by who owns the agent's process lifecycle.

Variables

View Source
var (
	ErrAgentNotConfigured = fmt.Errorf("agent is not configured")
	ErrAgentRouteTarget   = fmt.Errorf("agent is targeted by an agent route")
)

Functions

func BuiltinFactoryRegistered added in v0.5.0

func BuiltinFactoryRegistered(name string) bool

BuiltinFactoryRegistered reports whether a custom factory name is linked into this build.

func DecodeStoredAgentConfig

func DecodeStoredAgentConfig(data []byte) (any, error)

func RegisterBuiltinFactoryName added in v0.5.0

func RegisterBuiltinFactoryName(name string)

RegisterBuiltinFactoryName records a compiled-in custom agent factory name. Called by the host package's RegisterFactory; safe for concurrent use.

Types

type ACPRuntime

type ACPRuntime struct {
	AgentType       string                  `json:"agent_type"`
	CWD             string                  `json:"cwd"`
	AllowedRoots    []string                `json:"allowed_roots,omitempty"`
	DefaultModel    string                  `json:"default_model,omitempty"`
	Env             map[string]string       `json:"env,omitempty"`
	ConfigOverrides map[string]string       `json:"config_overrides,omitempty"`
	IdleTTL         time.Duration           `json:"idle_ttl,omitempty"`
	MaxInstances    int                     `json:"max_instances,omitempty"`
	PermissionMode  string                  `json:"permission_mode,omitempty"`
	Codex           *hostconfig.CodexConfig `json:"codex,omitempty"`
}

ACPRuntime is the Agent-owned ACP execution configuration. Agent identity, lifecycle metadata, and disabled state stay on Agent itself.

type Agent

type Agent struct {
	ID          string    `json:"id"`
	Name        string    `json:"name"`
	Description string    `json:"description,omitempty"`
	Runtime     Runtime   `json:"runtime"`
	Routes      Routes    `json:"routes"`
	Resources   Resources `json:"resources"`
	Policy      Policy    `json:"policy"`
	Disabled    bool      `json:"disabled"`
	CreatedAt   time.Time `json:"created_at"`
	UpdatedAt   time.Time `json:"updated_at"`
}

Agent is a first-class management object representing an operator-facing agent identity, not a protocol-specific service.

func (*Agent) Normalize

func (a *Agent) Normalize()

func (*Agent) NormalizeTimestamps

func (a *Agent) NormalizeTimestamps(now time.Time)

func (Agent) Validate

func (a Agent) Validate() error

type AgentRouteLookup added in v0.5.0

type AgentRouteLookup interface {
	AgentRouteIDsForAgent(ctx context.Context, agentID string) ([]string, error)
}

AgentRouteLookup resolves unified ingress routes that target an Agent. It is used to prevent deleting a definition while a route still references it.

type Budget

type Budget struct {
	MaxTurnsPerDay  int `json:"max_turns_per_day,omitempty"`
	MaxTokensPerDay int `json:"max_tokens_per_day,omitempty"`
}

type BuiltinAgentsMD added in v0.5.0

type BuiltinAgentsMD struct {
	Enabled bool `json:"enabled"`
	// Docs are the ordered virtual documents. Paths label the injected
	// sections and anchor @import references between docs; an @import that
	// resolves to no doc is skipped with a load warning, not an error.
	Docs []BuiltinAgentsMDDoc `json:"docs,omitempty"`
	// MaxTotalBytes caps the cumulative injected content; once exceeded,
	// remaining docs are skipped. 0 means no cap.
	MaxTotalBytes int `json:"max_total_bytes,omitempty"`
}

BuiltinAgentsMD enables the ADK agentsmd middleware over inline virtual documents: the content is injected transiently at model-call time, so it is excluded from summarization/reduction and never persisted to the session history. Docs are inline by design — a builtin agent has no workspace, and host filesystem paths would let a config-store object read arbitrary gateway-visible files into model context.

type BuiltinAgentsMDDoc added in v0.5.0

type BuiltinAgentsMDDoc struct {
	Path    string `json:"path"`
	Content string `json:"content"`
}

BuiltinAgentsMDDoc is one inline virtual document.

type BuiltinGeneration added in v0.5.0

type BuiltinGeneration struct {
	MaxTokens   int      `json:"max_tokens,omitempty"`
	Temperature *float32 `json:"temperature,omitempty"`
	TopP        *float32 `json:"top_p,omitempty"`
}

type BuiltinLimits added in v0.5.0

type BuiltinLimits struct {
	MaxConcurrentTurns int `json:"max_concurrent_turns,omitempty"`
	TurnTimeoutSeconds int `json:"turn_timeout_seconds,omitempty"`
}

BuiltinLimits bound a builtin agent's execution; both are fail-closed (reject, not queue). Zero values take the host defaults.

type BuiltinMiddlewares added in v0.5.0

type BuiltinMiddlewares struct {
	Summarization  *BuiltinSummarization  `json:"summarization,omitempty"`
	AgentsMD       *BuiltinAgentsMD       `json:"agentsmd,omitempty"`
	Reduction      *BuiltinReduction      `json:"reduction,omitempty"`
	ToolSearch     *BuiltinToolSearch     `json:"toolsearch,omitempty"`
	PlanTask       *BuiltinPlanTask       `json:"plantask,omitempty"`
	Skill          *BuiltinSkill          `json:"skill,omitempty"`
	PatchToolCalls *BuiltinPatchToolCalls `json:"patchtoolcalls,omitempty"`
}

BuiltinMiddlewares toggles the ADK middlewares that are safe as pure configuration; each is off by default. They apply to the root definition's chat-model nodes (a single node, the supervisor head, the deep head).

type BuiltinModel added in v0.5.0

type BuiltinModel struct {
	LLMRouteID string             `json:"llm_route_id"`
	Model      string             `json:"model,omitempty"`
	Retry      *BuiltinModelRetry `json:"retry,omitempty"`
}

BuiltinModel resolves through a gateway LLM route, never a raw provider, so credential scheduling, candidate fallback, and LLM usage events apply unchanged. Model is the route target name (logical model in model-target routes, upstream model in direct-provider routes); empty falls back to the route's default resolution.

type BuiltinModelRetry added in v0.5.0

type BuiltinModelRetry struct {
	// MaxRetries is the number of retry attempts after the initial call
	// (1..5).
	MaxRetries int `json:"max_retries"`
}

BuiltinModelRetry enables node-level model-call retry through the ADK retry wrapper (eino-reuse.md §4.3). It complements — not replaces — the gateway's candidate fallback: RoutedProvider advances between candidates within one call, while this retries the whole call after that fallback is exhausted. Retryability mirrors the gateway's failure classification (429 and 5xx retry; client-correctable errors fail immediately), and backoff keeps the ADK default. Sub-agents inherit it with the model reference. Not supported on planexecute role models: the eino prebuilt exposes no retry seam there, and a silent no-op is worse than a validation error.

type BuiltinPatchToolCalls added in v0.5.0

type BuiltinPatchToolCalls struct {
	Enabled bool `json:"enabled"`
}

BuiltinPatchToolCalls enables the ADK patchtoolcalls middleware: before every model call, tool calls in the history that have no corresponding tool result get a placeholder tool message inserted, so a history that ends up structurally incomplete never makes a strict upstream (tool_use/tool_result pairing) reject the request. Purely defensive — the host only commits successful turn transcripts, which are complete today.

type BuiltinPermissions added in v0.5.0

type BuiltinPermissions struct {
	// Mode is "auto_approve" (default — tools execute as the model asks) or
	// "interactive" (each MCP tool call needs an explicit decision).
	Mode string `json:"mode,omitempty"`
	// TimeoutSeconds is the pending-decision TTL. Zero takes the host default.
	TimeoutSeconds int `json:"timeout_seconds,omitempty"`
	// MaxPending caps simultaneously pending permissions for the agent.
	// Pending permissions hold no turn slots, so without a cap they could
	// accumulate without bound. Zero takes the host default.
	MaxPending int `json:"max_pending,omitempty"`
	// AutoApproveTools bypasses interactive gating for fully-qualified
	// "<mcp_service_id>/<tool_name>" entries (bare names could collide across
	// services). Every entry must resolve to a declared tool selection.
	AutoApproveTools []string `json:"auto_approve_tools,omitempty"`
}

BuiltinPermissions is the root-level human-in-the-loop policy over the definition's MCP tool executions; it applies to every topology node. Gateway-local middleware tools (skill, plantask task tools, tool_search) are always exempt. In interactive mode an unapproved tool call suspends the turn through an ADK checkpoint interrupt — no turn slot, stream, or goroutine is held while a human decides — and resumes through the turn endpoint. Every lifecycle edge (decision timeout, definition update, pending capacity, unanswered calls) fails closed.

func (*BuiltinPermissions) Interactive added in v0.5.0

func (p *BuiltinPermissions) Interactive() bool

Interactive reports whether the definition gates tool executions.

type BuiltinPlanExecute added in v0.5.0

type BuiltinPlanExecute struct {
	Planner   *BuiltinPlanExecuteRole `json:"planner,omitempty"`
	Executor  *BuiltinPlanExecuteRole `json:"executor,omitempty"`
	Replanner *BuiltinPlanExecuteRole `json:"replanner,omitempty"`
}

BuiltinPlanExecute configures the three planexecute roles. The planner and replanner emit structured plans through tool calling, so their models must be tool-capable; the executor is the only role that runs MCP tools.

type BuiltinPlanExecuteRole added in v0.5.0

type BuiltinPlanExecuteRole struct {
	Model      *BuiltinModel      `json:"model,omitempty"`
	Generation *BuiltinGeneration `json:"generation,omitempty"`
	// Tools replace the enclosing node's tool selection for the executor.
	Tools []BuiltinToolSelection `json:"tools,omitempty"`
	// MaxIterations bounds the executor's inner tool-call loop; 0 uses the
	// ADK default.
	MaxIterations int `json:"max_iterations,omitempty"`
}

BuiltinPlanExecuteRole overrides one planexecute role. Tools and MaxIterations are executor-only: the planner and replanner interact with the model through the prebuilt's fixed plan/respond tool schemas and cannot carry MCP tools.

type BuiltinPlanTask added in v0.5.0

type BuiltinPlanTask struct {
	Enabled bool `json:"enabled"`
}

BuiltinPlanTask enables the ADK plantask middleware: the model gets TaskCreate/TaskGet/TaskUpdate/TaskList tools for maintaining a structured task list. The task board is stored in the session (in-memory, session-scoped), so it shares the session's restart-loss and eviction semantics and never leaks between conversations.

type BuiltinReduction added in v0.5.0

type BuiltinReduction struct {
	Enabled bool `json:"enabled"`
	// MaxTokensForClear is the estimated-token threshold (a chars/4
	// heuristic, not a tokenizer count) that activates clearing; 0 uses the
	// ADK default.
	MaxTokensForClear int `json:"max_tokens_for_clear,omitempty"`
	// ClearRetentionSuffixLimit keeps the most recent N tool-calling
	// exchanges uncleared; 0 uses the ADK default of 1.
	ClearRetentionSuffixLimit int `json:"clear_retention_suffix_limit,omitempty"`
	// ClearExcludeTools lists tool names whose calls are never cleared.
	ClearExcludeTools []string `json:"clear_exclude_tools,omitempty"`
}

BuiltinReduction enables the clear phase of the ADK tool-reduction middleware: when the estimated context exceeds the token threshold, older tool-call arguments and outputs are replaced with placeholders. Clearing is lossy — a builtin agent has no file backend to offload cleared content to, so the truncation/offload phase stays disabled.

type BuiltinRuntime added in v0.5.0

type BuiltinRuntime struct {
	Model        BuiltinModel           `json:"model"`
	SystemPrompt string                 `json:"system_prompt,omitempty"`
	Generation   *BuiltinGeneration     `json:"generation,omitempty"`
	Tools        []BuiltinToolSelection `json:"tools,omitempty"`
	Topology     BuiltinTopology        `json:"topology"`
	Middlewares  *BuiltinMiddlewares    `json:"middlewares,omitempty"`
	Permissions  *BuiltinPermissions    `json:"permissions,omitempty"`
	Limits       *BuiltinLimits         `json:"limits,omitempty"`
}

BuiltinRuntime is the persisted definition of an ADK-hosted agent: the gateway ships one generic ADK host, and "starting" the agent means the host materializes the ADK object graph from this definition (§5.7.1).

type BuiltinSkill added in v0.5.0

type BuiltinSkill struct {
	Enabled bool `json:"enabled"`
	// Skills are the selectable inline skills.
	Skills []BuiltinSkillDoc `json:"skills,omitempty"`
}

BuiltinSkill enables the ADK skill middleware over inline virtual skills: the model gets a skill tool whose description advertises every skill's name and description, and invoking it returns the skill's instructions as the tool result. Inline execution only — the definition exposes no context/ agent/model frontmatter, so fork-mode sub-agent execution and per-skill model overrides are structurally impossible. Skills are inline for the same reason agentsmd docs are: a builtin agent has no workspace, and host filesystem paths would let a config-store object read arbitrary gateway-visible files into model context.

type BuiltinSkillDoc added in v0.5.0

type BuiltinSkillDoc struct {
	Name        string `json:"name"`
	Description string `json:"description,omitempty"`
	Content     string `json:"content"`
}

BuiltinSkillDoc is one inline skill: the name the model selects, the description advertised in the skill tool, and the markdown instructions returned when the skill is invoked.

type BuiltinSubAgent added in v0.5.0

type BuiltinSubAgent struct {
	Name         string                 `json:"name"`
	Description  string                 `json:"description,omitempty"`
	Model        *BuiltinModel          `json:"model,omitempty"`
	SystemPrompt string                 `json:"system_prompt,omitempty"`
	Generation   *BuiltinGeneration     `json:"generation,omitempty"`
	Tools        []BuiltinToolSelection `json:"tools,omitempty"`
	// Topology of the sub-agent itself; nil means a single chat-model agent.
	Topology *BuiltinTopology `json:"topology,omitempty"`
}

BuiltinSubAgent is a nested definition object (same schema, minus limits). It exists only as an internal node of the enclosing agent: no first-class identity, no separate usage attribution, no admin surface. Model is optional and inherits the parent definition's model when nil.

type BuiltinSummarization added in v0.5.0

type BuiltinSummarization struct {
	Enabled bool `json:"enabled"`
	// TriggerTokens overrides the token threshold that activates
	// summarization; 0 uses the ADK default.
	TriggerTokens int `json:"trigger_tokens,omitempty"`
}

BuiltinSummarization enables the ADK context-compaction middleware using the agent's own chat model for summary generation.

type BuiltinToolSearch added in v0.5.0

type BuiltinToolSearch struct {
	Enabled bool `json:"enabled"`
}

BuiltinToolSearch enables the ADK dynamictool/toolsearch middleware: the node's MCP tools are withheld from the model's tool list and exposed through a tool_search meta-tool the model queries to load tools on demand. Useful when the referenced MCP services expose many tools. Client-side search only — the model-native variant needs deferred-tool support the gateway's providers do not expose. The tool list changes between calls as tools are loaded, which can invalidate the upstream prompt cache.

type BuiltinToolSelection added in v0.5.0

type BuiltinToolSelection struct {
	MCPServiceID string   `json:"mcp_service_id"`
	Tools        []string `json:"tools,omitempty"`
}

BuiltinToolSelection references a gateway-managed MCP service. Tools lists the allowed tool names; empty means every tool the service exposes. Selection by name is fail-closed at materialization time.

type BuiltinTopology added in v0.5.0

type BuiltinTopology struct {
	Kind string `json:"kind"`
	// Factory names a compiled-in custom agent factory; required and only
	// meaningful when Kind is "custom".
	Factory string `json:"factory,omitempty"`
	// MaxIterations bounds the topology's iteration loop: loop rounds for
	// kind loop, execute-replan rounds for kind planexecute, and reasoning
	// iterations for kind deep. 0 uses the ADK default.
	MaxIterations int               `json:"max_iterations,omitempty"`
	SubAgents     []BuiltinSubAgent `json:"sub_agents,omitempty"`
	// PlanExecute overrides the planexecute role nodes; only meaningful when
	// Kind is "planexecute". Every role inherits the enclosing node's model
	// when unset, and the executor inherits the enclosing node's tools when
	// it declares none, so the whole block is optional.
	PlanExecute *BuiltinPlanExecute `json:"plan_execute,omitempty"`
}

BuiltinTopology selects the ADK structure. Sub-agents are inline child definitions only — never references to other Agent objects (§5.7.2).

type DefinitionCleanup added in v0.5.0

type DefinitionCleanup func(context.Context)

DefinitionCleanup performs slow or external cleanup after a definition generation is dispatchable. The manager supplies a detached, bounded context; cleanup must not rely on the originating request remaining alive.

type DefinitionCommit added in v0.5.0

type DefinitionCommit func() DefinitionCleanup

DefinitionCommit publishes prepared runtime state while new snapshot reads are excluded. It must be bounded and in-memory only: it must not perform config-store, process, transport, or other external I/O, and it must not call Manager.GetSnapshot, Snapshot, HasAgent, or SnapshotGeneration because the snapshot mutex is not reentrant. Slow cleanup is returned for execution after the snapshot lock is released.

type DefinitionListener added in v0.5.0

type DefinitionListener func(ctx context.Context, agents []Agent) DefinitionCommit

DefinitionListener prepares derived state for a prospective definition generation before the snapshot write lock is acquired. The agents slice is a deep clone owned by the listener call and never aliases the generation. Store reads and other preparation belong here; external cleanup does not.

type HTTPRuntime

type HTTPRuntime struct {
	Endpoint string `json:"endpoint"`
	AuthRef  string `json:"auth_ref,omitempty"`
}

HTTPRuntime carries the agent-level endpoint and callback auth for an agent that owns its own lifecycle.

type Manager

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

Manager owns agent CRUD, the deep-cloned definition snapshot used by per-request dispatch, and the in-memory resource-route -> agent index used for write-time attribution. The snapshot and index are rebuilt on every mutation and never read from the config store on the hot path.

func NewManager

func NewManager(store configstore.ConfigStore) *Manager

func (*Manager) AddDefinitionListener added in v0.5.0

func (m *Manager) AddDefinitionListener(listener DefinitionListener)

AddDefinitionListener registers a listener prepared and committed for every generation publication (Refresh, Create, Update, Delete). Registration is expected during bootstrap, before concurrent snapshot reads begin.

func (*Manager) Create

func (m *Manager) Create(ctx context.Context, a Agent) error

func (*Manager) Delete

func (m *Manager) Delete(ctx context.Context, id string) error

func (*Manager) Get

func (m *Manager) Get(ctx context.Context, id string) (Agent, error)

func (*Manager) GetSnapshot added in v0.5.0

func (m *Manager) GetSnapshot(id string) (Agent, bool)

GetSnapshot returns one Agent definition from the current immutable generation without touching the config store. The returned value is a deep clone: mutating it (including through Runtime pointers) cannot corrupt the generation. It is the required lookup for per-request dispatch paths.

func (*Manager) HasAgent added in v0.5.0

func (m *Manager) HasAgent(id string) bool

HasAgent reports definition existence from the current generation. It backs AgentRoute target validation; disabled Agents still exist.

func (*Manager) List

func (m *Manager) List(ctx context.Context) ([]Agent, error)

func (*Manager) Recommit added in v0.5.0

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

Recommit republishes the already-loaded definition generation without reading the Agent store. It is used when an external runtime record changes and definition listeners must rebuild derived runtime snapshots.

func (*Manager) Refresh

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

Refresh decodes and deep-clones the complete store result into a fresh definition generation, then commits it atomically. The derived resource-route -> agent attribution index is rebuilt as part of the commit.

func (*Manager) ResolveAgentID

func (m *Manager) ResolveAgentID(routeID, serviceID, sessionID string) (string, bool)

ResolveAgentID maps an originating resource route back to a single agent for write-time usage attribution. The service/session arguments remain part of the protocol-neutral usage seam for MCP callers but do not identify an Agent. It returns ok=false when the route mapping is empty.

func (*Manager) SetRouteLookup

func (m *Manager) SetRouteLookup(lookup AgentRouteLookup)

SetRouteLookup wires the optional unified AgentRoute reference lookup.

func (*Manager) Snapshot added in v0.5.0

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

Snapshot returns a deep-cloned view of every Agent in the current generation, sorted by id, without touching the config store.

func (*Manager) SnapshotGeneration added in v0.5.0

func (m *Manager) SnapshotGeneration() uint64

SnapshotGeneration returns the committed generation counter. It only moves forward and increments once per commit, letting tests and diagnostics prove atomic replacement.

func (*Manager) Update

func (m *Manager) Update(ctx context.Context, id string, a Agent) error

type Policy

type Policy struct {
	MaxAgentDepth int     `json:"max_agent_depth,omitempty"`
	Budget        *Budget `json:"budget,omitempty"`
}

Policy holds runtime-agnostic governance only. Runtime-specific config belongs under runtime.<type>.

type Resources

type Resources struct {
	ProviderIDs   []string `json:"provider_ids,omitempty"`
	MCPServiceIDs []string `json:"mcp_service_ids,omitempty"`
	VirtualKeyIDs []string `json:"virtual_key_ids,omitempty"`
}

Resources is a management view of what the agent is allowed to use. It is not enforced inline on the data-plane request path in P0/P1.

type Routes

type Routes struct {
	LLMRouteIDs []string `json:"llm_route_ids,omitempty"`
	MCPRouteIDs []string `json:"mcp_route_ids,omitempty"`
}

Routes are management/display references used to surface matching ingress routes and to drive attribution; they do not select the execution backend.

type Runtime

type Runtime struct {
	Type    string          `json:"type"`
	ACP     *ACPRuntime     `json:"acp,omitempty"`
	HTTP    *HTTPRuntime    `json:"http,omitempty"`
	Builtin *BuiltinRuntime `json:"builtin,omitempty"`
}

Runtime is authoritative for execution. It binds the agent to exactly one runtime backend instance, selected by Type.

Directories

Path Synopsis
Package builtin is the generic ADK host for builtin-runtime agents: the gateway ships this one host compiled into agw/agwd, and a builtin agent is a persisted definition (agent.BuiltinRuntime) that the host materializes into an eino ADK object graph on demand.
Package builtin is the generic ADK host for builtin-runtime agents: the gateway ships this one host compiled into agw/agwd, and a builtin agent is a persisted definition (agent.BuiltinRuntime) that the host materializes into an eino ADK object graph on demand.
Package runtime defines the runtime-neutral execution boundary for operator-facing Agents.
Package runtime defines the runtime-neutral execution boundary for operator-facing Agents.
runtimetest
Package runtimetest provides reusable fake runtime backends for dispatcher and Admin contract tests.
Package runtimetest provides reusable fake runtime backends for dispatcher and Admin contract tests.

Jump to

Keyboard shortcuts

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