Documentation
¶
Overview ¶
Package protocol implements the admin-scoped `agent_config.*` Protocol methods the Console agent-config control plane consumes:
- agent_config.get — read an agent's active config revision.
- agent_config.set_revision — write a new immutable revision.
- agent_config.list_revisions — the agent's revision chain.
- agent_config.diff — server-side compare of two revisions.
- agent_config.rollback — repoint the active pointer.
- agent_config.skills.{list,upsert,delete} — skills control (the first consumer of the registry primitive; see skills.go).
Every write is admin-scoped (the verified `auth.ScopeAdmin` claim, enforced at the wire handler — the agent-config authorization model) and identity-mandatory. A config edit applies to the agent's NEXT run (next-turn projection — never mid-flight, per the concurrent-reuse contract).
The seam (CLAUDE.md §4.4) ¶
The Service depends on the narrow `agentcfg.Registry` interface (the StateStore-backed concrete satisfies it) plus an optional `skills.SkillStore` for the skills consumer; tests inject fakes. The Service owns wire validation + the wire↔domain mapping; the registry owns persistence + the revision events; the SkillStore owns the skill rows + the skill events.
Identity is mandatory (CLAUDE.md §6 rule 9) ¶
Every method takes the wire request's IdentityScope. An incomplete triple fails closed with ErrIdentityRequired. The handler overlays the verified identity onto the request body, so a caller cannot target another identity's config through this surface.
Concurrent reuse ¶
A constructed *Service is immutable after NewService and safe to share across N goroutines: it holds only the registry / skill-store references + a clock + logger. Per-call state lives in arguments and locals.
Index ¶
- Variables
- type AttachRequest
- type Clock
- type ConnectionAttacher
- type ConnectionState
- type Option
- func WithBus(b events.EventBus) Option
- func WithClock(c Clock) Option
- func WithConnectionAttacher(a ConnectionAttacher) Option
- func WithCoordinator(c pauseresume.Coordinator) Option
- func WithLogger(l *slog.Logger) Option
- func WithSessionOverlay(st sessionoverlay.Store) Option
- func WithSkillStore(st skills.SkillStore) Option
- func WithStdioAllowlist(commands []string) Option
- func WithValidModels(models []string) Option
- type Service
- func (s *Service) AddMCPConnection(ctx context.Context, req prototypes.AgentConfigAddMCPConnectionRequest) (prototypes.AgentConfigAddMCPConnectionResponse, error)
- func (s *Service) Diff(ctx context.Context, req prototypes.AgentConfigDiffRequest) (prototypes.AgentConfigDiffResponse, error)
- func (s *Service) Get(ctx context.Context, req prototypes.AgentConfigGetRequest) (prototypes.AgentConfigGetResponse, error)
- func (s *Service) ListRevisions(ctx context.Context, req prototypes.AgentConfigListRevisionsRequest) (prototypes.AgentConfigListRevisionsResponse, error)
- func (s *Service) Rollback(ctx context.Context, req prototypes.AgentConfigRollbackRequest) (prototypes.AgentConfigRollbackResponse, error)
- func (s *Service) SessionSetSourceDisables(ctx context.Context, req prototypes.AgentConfigSessionSetSourceDisablesRequest) (prototypes.AgentConfigSessionSetSourceDisablesResponse, error)
- func (s *Service) SessionSetUserPrompt(ctx context.Context, req prototypes.AgentConfigSessionSetUserPromptRequest) (prototypes.AgentConfigSessionSetUserPromptResponse, error)
- func (s *Service) SessionSkillsDelete(ctx context.Context, req prototypes.AgentConfigSessionSkillsDeleteRequest) (prototypes.AgentConfigSessionSkillsDeleteResponse, error)
- func (s *Service) SessionSkillsList(ctx context.Context, req prototypes.AgentConfigSessionSkillsListRequest) (prototypes.AgentConfigSessionSkillsListResponse, error)
- func (s *Service) SessionSkillsUpsert(ctx context.Context, req prototypes.AgentConfigSessionSkillsUpsertRequest) (prototypes.AgentConfigSessionSkillsUpsertResponse, error)
- func (s *Service) SetLLMParams(ctx context.Context, req prototypes.AgentConfigSetLLMParamsRequest) (prototypes.AgentConfigSetLLMParamsResponse, error)
- func (s *Service) SetPromptLayers(ctx context.Context, req prototypes.AgentConfigSetPromptLayersRequest) (prototypes.AgentConfigSetPromptLayersResponse, error)
- func (s *Service) SetRevision(ctx context.Context, req prototypes.AgentConfigSetRevisionRequest) (prototypes.AgentConfigSetRevisionResponse, error)
- func (s *Service) SetToolExposure(ctx context.Context, req prototypes.AgentConfigSetToolExposureRequest) (prototypes.AgentConfigSetToolExposureResponse, error)
- func (s *Service) SkillsDelete(ctx context.Context, req prototypes.AgentConfigSkillsDeleteRequest) (prototypes.AgentConfigSkillsDeleteResponse, error)
- func (s *Service) SkillsList(ctx context.Context, req prototypes.AgentConfigSkillsListRequest) (prototypes.AgentConfigSkillsListResponse, error)
- func (s *Service) SkillsUpsert(ctx context.Context, req prototypes.AgentConfigSkillsUpsertRequest) (prototypes.AgentConfigSkillsUpsertResponse, error)
- func (s *Service) UserDiff(ctx context.Context, req prototypes.AgentConfigUserDiffRequest) (prototypes.AgentConfigUserDiffResponse, error)
- func (s *Service) UserGet(ctx context.Context, req prototypes.AgentConfigUserGetRequest) (prototypes.AgentConfigUserGetResponse, error)
- func (s *Service) UserListRevisions(ctx context.Context, req prototypes.AgentConfigUserListRevisionsRequest) (prototypes.AgentConfigUserListRevisionsResponse, error)
- func (s *Service) UserRollback(ctx context.Context, req prototypes.AgentConfigUserRollbackRequest) (prototypes.AgentConfigUserRollbackResponse, error)
- func (s *Service) UserSetRevision(ctx context.Context, req prototypes.AgentConfigUserSetRevisionRequest) (prototypes.AgentConfigUserSetRevisionResponse, error)
Constants ¶
This section is empty.
Variables ¶
var ( // ErrAuthRequired — the attacher signals the MCP server needs // authorization. The service parks on the unified pause/resume primitive. // Concrete attachers wrap this so the service reaches it via errors.Is. ErrAuthRequired = errors.New("agentcfg/protocol: mcp connection requires authorization") // ConnectionAttacher is wired on this runtime (→ 501). ErrConnectionAttachUnavailable = errors.New("agentcfg/protocol: mcp connection attach not wired on this runtime") // ErrStdioNotAllowed — a stdio add named a command absent from the // fail-closed allowlist (→ 403). Adding a stdio server is the most // privileged action; the allowlist is the §7 RCE gate. ErrStdioNotAllowed = errors.New("agentcfg/protocol: stdio mcp connection command is not allowlisted") // ErrInvalidConnection — the connection descriptor failed validation // (empty name, unknown transport, missing/incoherent command/URL) (→ 400). ErrInvalidConnection = errors.New("agentcfg/protocol: invalid mcp connection descriptor") // pause/resume Coordinator is wired (→ 500). Fail loud rather than drop // the auth requirement silently (CLAUDE.md §13). ErrCoordinatorUnavailable = errors.New("agentcfg/protocol: pause/resume coordinator not wired for the mcp oauth path") )
Add-connection sentinel errors. The wire handler maps each onto a canonical Protocol Code + HTTP status; in-process callers compare with errors.Is.
var ( // ErrIdentityRequired — the request carried an incomplete identity // triple or an empty agent id. Fails closed. ErrIdentityRequired = errors.New("agentcfg/protocol: identity scope incomplete") // ErrMisconfigured — NewService was called with a nil registry. ErrMisconfigured = errors.New("agentcfg/protocol: NewService missing a mandatory dependency") // was wired into the Service. ErrSkillsUnavailable = errors.New("agentcfg/protocol: skills control not wired on this runtime") // ErrUnknownModel — a set_llm_params (or a set_revision carrying an // LLMParams.Model) named a model with no configured ModelProfile. The // per-agent model is validated at set time (parity with the tenant // model-swap), so an invalid model can never be persisted — fail loud, // never a silent fallback (CLAUDE.md §13). ErrUnknownModel = errors.New("agentcfg/protocol: unknown model (no configured ModelProfile)") // ErrInvalidLLMParams — a set_llm_params (or a set_revision carrying an // LLMParams section) supplied an out-of-range sampling value (temperature // outside [0,2], non-positive max-tokens, or an unknown reasoning-effort). // Validated at set time so an invalid value never reaches a run (parity // with runs.set_overrides), fail loud (CLAUDE.md §13). ErrInvalidLLMParams = errors.New("agentcfg/protocol: invalid LLM parameters") )
Sentinel errors the Service returns. The wire handler maps each onto a canonical Protocol Code + HTTP status; in-process callers compare with errors.Is.
ErrSessionOverlayUnavailable — a session-safe method was called but no session-overlay store was wired into the Service.
Functions ¶
This section is empty.
Types ¶
type AttachRequest ¶
type AttachRequest struct {
// Identity is the verified caller triple (the attach runs under it; the
// driver stamps it on transport-side events).
Identity identity.Identity
// Name is the unique MCP source id.
Name string
// Transport is "stdio" or "http".
Transport agentcfg.MCPTransport
// Command is the stdio argv (argv[0] is the binary; no shell).
Command []string
// URL is the http(s) endpoint.
URL string
// Headers are operator-supplied auth headers used ONLY for the live
// attach. SECRET — never persisted (CLAUDE.md §7).
Headers map[string]string
}
AttachRequest is the input to a ConnectionAttacher. It carries the non-secret descriptor PLUS the optional operator-supplied auth headers used ONLY for the live transport — the attacher never persists them.
type ConnectionAttacher ¶
type ConnectionAttacher interface {
Attach(ctx context.Context, req AttachRequest) error
}
ConnectionAttacher drives the real MCP attach lifecycle for a runtime add: dial → `initialize` handshake → discover → register, fail-loud per step. It is the seam that keeps the concrete MCP driver out of this package (the §4.4 boundary): the cmd/harbor + devstack boundary injects a concrete that builds a `config.MCPServerConfig` and calls the MCP driver's `Attach`.
Attach returns:
- nil on a successful attach (online — the server's tools are registered on the live catalog);
- an error wrapping ErrAuthRequired when the server needs authorization (the service parks on the unified pause/resume primitive);
- any other error when an attach step failed (the service records the `failed` lifecycle, never a half-attached server).
type ConnectionState ¶
type ConnectionState string
ConnectionState is the explicit attach lifecycle state surfaced on the response + the lifecycle events. The set is closed.
const ( // ConnectionStatePending — the attach has begun (dial → handshake → // discover → register). The transient initial state. ConnectionStatePending ConnectionState = "pending" // ConnectionStateOnline — the attach completed; the server's tools are // registered on the live catalog. ConnectionStateOnline ConnectionState = "online" // ConnectionStateFailed — an attach step failed; no revision recorded, // no half-attached server registered. ConnectionStateFailed ConnectionState = "failed" // ConnectionStateAuthRequired — the attach parked on the unified // pause/resume primitive awaiting authorization. NOTE: the resume // continuation that re-drives the attach to online is not yet // implemented — resume currently only releases the pause (tracked in // issue #375). The descriptor revision is recorded; the server comes // online on a subsequent (authorized) add. ConnectionStateAuthRequired ConnectionState = "auth_required" )
The canonical attach lifecycle states.
type Option ¶
type Option func(*Service)
Option configures NewService.
func WithBus ¶
WithBus wires the EventBus the tool-exposure consumer publishes the `mcp.connection.paused` / `.resumed` overlay events through. A nil bus leaves those events unpublished (the revision is still recorded — the generic `agent.config.revised` still fires from the registry).
func WithConnectionAttacher ¶
func WithConnectionAttacher(a ConnectionAttacher) Option
WithConnectionAttacher wires the concrete that drives the real MCP attach lifecycle for `agent_config.add_mcp_connection`. A nil attacher leaves the method returning ErrConnectionAttachUnavailable (→ 501 at the wire edge). The concrete (which imports the MCP driver) is injected at the cmd/harbor + devstack boundary; this package depends only on the interface.
func WithCoordinator ¶
func WithCoordinator(c pauseresume.Coordinator) Option
WithCoordinator wires the unified pause/resume primitive an auth-required MCP attach parks on. A nil coordinator leaves an auth-required attach failing loud with ErrCoordinatorUnavailable (never a silent drop).
func WithLogger ¶
WithLogger sets the slog.Logger. A nil logger routes to slog.Default().
func WithSessionOverlay ¶
func WithSessionOverlay(st sessionoverlay.Store) Option
WithSessionOverlay wires the session-scoped safe-subset overlay store so the NON-admin `agent_config.session.*` verbs are live. A nil store leaves those methods returning ErrSessionOverlayUnavailable (→ 501 at the wire edge). The store is keyed by the caller's real (tenant, user, session) triple, so a session's overlay is invisible to another session.
func WithSkillStore ¶
func WithSkillStore(st skills.SkillStore) Option
WithSkillStore wires the SkillStore so the `agent_config.skills.*` methods are live. A nil store leaves the skills methods returning ErrSkillsUnavailable (→ 501 at the wire edge).
func WithStdioAllowlist ¶
WithStdioAllowlist sets the fail-closed allowlist of permitted stdio commands (matched on argv[0]) for `agent_config.add_mcp_connection`. A stdio add whose command[0] is absent is rejected with ErrStdioNotAllowed. An empty / nil allowlist rejects EVERY stdio add (the secure default); http adds are unaffected. Adding a stdio server runs an operator-supplied command (an RCE surface) — this gate is the §7 fail-closed boundary.
func WithValidModels ¶
WithValidModels sets the set of model names with a configured ModelProfile. `set_llm_params` (and a `set_revision` carrying an LLMParams.Model) reject a model outside this set with ErrUnknownModel — parity with the tenant model-swap, validated at SET time so an invalid model can never be persisted (never a silent run-start fallback).
An empty / nil set means model-pinning is UNAVAILABLE: every non-empty model set fails loud (the binary always wires at least the bound default model, so an empty set signals a misconfiguration, not "accept anything"). This matches the governance tenant-override policy's fail-loud-on-empty stance; it deliberately DIFFERS from runs.set_overrides (the one-shot session swap), which accepts any model when no set is configured.
type Service ¶
type Service struct {
// contains filtered or unexported fields
}
Service implements the admin-scoped agent-config methods.
func NewService ¶
NewService builds the agent-config Service over a Registry. registry is mandatory — a nil fails loud with ErrMisconfigured rather than building a Service that would nil-panic on the first request (CLAUDE.md §5).
The returned *Service is immutable after construction and safe for concurrent use by N goroutines.
func (*Service) AddMCPConnection ¶
func (s *Service) AddMCPConnection(ctx context.Context, req prototypes.AgentConfigAddMCPConnectionRequest) (prototypes.AgentConfigAddMCPConnectionResponse, error)
AddMCPConnection adds a NEW MCP server connection. See the file doc for the full lifecycle, secret-hygiene, and stdio-gate contract.
func (*Service) Diff ¶
func (s *Service) Diff(ctx context.Context, req prototypes.AgentConfigDiffRequest) (prototypes.AgentConfigDiffResponse, error)
Diff returns the server-side compare of two existing revisions.
func (*Service) Get ¶
func (s *Service) Get(ctx context.Context, req prototypes.AgentConfigGetRequest) (prototypes.AgentConfigGetResponse, error)
Get reads the agent's active config revision.
func (*Service) ListRevisions ¶
func (s *Service) ListRevisions(ctx context.Context, req prototypes.AgentConfigListRevisionsRequest) (prototypes.AgentConfigListRevisionsResponse, error)
ListRevisions returns the agent's revision chain, newest-first.
func (*Service) Rollback ¶
func (s *Service) Rollback(ctx context.Context, req prototypes.AgentConfigRollbackRequest) (prototypes.AgentConfigRollbackResponse, error)
Rollback repoints the active pointer to an existing revision.
func (*Service) SessionSetSourceDisables ¶
func (s *Service) SessionSetSourceDisables(ctx context.Context, req prototypes.AgentConfigSessionSetSourceDisablesRequest) (prototypes.AgentConfigSessionSetSourceDisablesResponse, error)
SessionSetSourceDisables records the session's narrow-only disable set (servers + tools). There is intentionally no enable field: the set names what the session wants OFF, and the run-start projection unions it into the admin exclusion set — so a session edit can only narrow the admin-allowed exposure, never widen it.
func (*Service) SessionSetUserPrompt ¶
func (s *Service) SessionSetUserPrompt(ctx context.Context, req prototypes.AgentConfigSessionSetUserPromptRequest) (prototypes.AgentConfigSessionSetUserPromptResponse, error)
SessionSetUserPrompt sets ONLY the session's user prompt layer. The session-writable shape carries no base field, so this can never alter the operator base — base-unwritable-by-session is structural.
func (*Service) SessionSkillsDelete ¶
func (s *Service) SessionSkillsDelete(ctx context.Context, req prototypes.AgentConfigSessionSkillsDeleteRequest) (prototypes.AgentConfigSessionSkillsDeleteResponse, error)
SessionSkillsDelete deletes a personal skill under the caller's real triple and removes its name from the session overlay.
func (*Service) SessionSkillsList ¶
func (s *Service) SessionSkillsList(ctx context.Context, req prototypes.AgentConfigSessionSkillsListRequest) (prototypes.AgentConfigSessionSkillsListResponse, error)
SessionSkillsList lists the session's skills (metadata only) under the caller's real triple. The SkillStore is already session-scoped, so the list is naturally isolated to the caller's session.
func (*Service) SessionSkillsUpsert ¶
func (s *Service) SessionSkillsUpsert(ctx context.Context, req prototypes.AgentConfigSessionSkillsUpsertRequest) (prototypes.AgentConfigSessionSkillsUpsertResponse, error)
SessionSkillsUpsert upserts an EPHEMERAL personal skill under the caller's real triple and records its name in the session overlay (so the run-start projection adds it back above the admin membership filter). The skill scope is FORCED to session — a session personal skill never promotes to the agent/tenant scope.
func (*Service) SetLLMParams ¶
func (s *Service) SetLLMParams(ctx context.Context, req prototypes.AgentConfigSetLLMParamsRequest) (prototypes.AgentConfigSetLLMParamsResponse, error)
SetLLMParams records a new config revision pinning the supplied per-agent LLM-parameter section (model / temperature / max-tokens / reasoning-effort) as a desired-state replace of the LLM-params section. A set Model is validated against the configured ModelProfiles (an unknown model is rejected with ErrUnknownModel). The prompt-layer + skills + tool-exposure + connection sections of the active revision are carried forward unchanged.
func (*Service) SetPromptLayers ¶
func (s *Service) SetPromptLayers(ctx context.Context, req prototypes.AgentConfigSetPromptLayersRequest) (prototypes.AgentConfigSetPromptLayersResponse, error)
SetPromptLayers records a new config revision pinning the supplied layered system prompt (operator base and/or user layer) as a desired-state replace of the prompt-layer section. The skills + tool-exposure sections of the active revision are carried forward unchanged.
func (*Service) SetRevision ¶
func (s *Service) SetRevision(ctx context.Context, req prototypes.AgentConfigSetRevisionRequest) (prototypes.AgentConfigSetRevisionResponse, error)
SetRevision writes a new immutable revision and advances the active pointer.
func (*Service) SetToolExposure ¶
func (s *Service) SetToolExposure(ctx context.Context, req prototypes.AgentConfigSetToolExposureRequest) (prototypes.AgentConfigSetToolExposureResponse, error)
SetToolExposure records a new config revision pinning the supplied MCP-exposure desired state (paused servers + disabled tools) and emits an `mcp.connection.paused` / `.resumed` event for each server whose pause state changed relative to the prior active revision. The skills + prompt sections of the active revision are carried forward unchanged.
func (*Service) SkillsDelete ¶
func (s *Service) SkillsDelete(ctx context.Context, req prototypes.AgentConfigSkillsDeleteRequest) (prototypes.AgentConfigSkillsDeleteResponse, error)
SkillsDelete deletes a skill from the SkillStore and records the membership change as a config revision.
func (*Service) SkillsList ¶
func (s *Service) SkillsList(ctx context.Context, req prototypes.AgentConfigSkillsListRequest) (prototypes.AgentConfigSkillsListResponse, error)
SkillsList returns the agent's skills (metadata only) from the SkillStore under the caller's identity.
func (*Service) SkillsUpsert ¶
func (s *Service) SkillsUpsert(ctx context.Context, req prototypes.AgentConfigSkillsUpsertRequest) (prototypes.AgentConfigSkillsUpsertResponse, error)
SkillsUpsert upserts a skill into the SkillStore and records the membership change as a config revision. A pack-overwrite refusal surfaces as the typed `skills.ErrPackOverwriteRefused` (mapped to CodeInvalidRequest at the wire edge) — never a silent overwrite.
func (*Service) UserDiff ¶ added in v1.6.0
func (s *Service) UserDiff(ctx context.Context, req prototypes.AgentConfigUserDiffRequest) (prototypes.AgentConfigUserDiffResponse, error)
UserDiff returns the server-side compare of two existing revisions of the caller's own variant.
func (*Service) UserGet ¶ added in v1.6.0
func (s *Service) UserGet(ctx context.Context, req prototypes.AgentConfigUserGetRequest) (prototypes.AgentConfigUserGetResponse, error)
UserGet reads the caller's own durable config variant active revision.
func (*Service) UserListRevisions ¶ added in v1.6.0
func (s *Service) UserListRevisions(ctx context.Context, req prototypes.AgentConfigUserListRevisionsRequest) (prototypes.AgentConfigUserListRevisionsResponse, error)
UserListRevisions returns the caller's own variant revision chain, newest-first.
func (*Service) UserRollback ¶ added in v1.6.0
func (s *Service) UserRollback(ctx context.Context, req prototypes.AgentConfigUserRollbackRequest) (prototypes.AgentConfigUserRollbackResponse, error)
UserRollback repoints the caller's own variant active pointer to an existing revision WITHOUT mutating any revision. Serialised per-owner.
func (*Service) UserSetRevision ¶ added in v1.6.0
func (s *Service) UserSetRevision(ctx context.Context, req prototypes.AgentConfigUserSetRevisionRequest) (prototypes.AgentConfigUserSetRevisionResponse, error)
UserSetRevision writes a new immutable revision of the caller's durable variant from the bounded safe-subset payload and advances the active pointer. Serialised per-owner (scope, tenant, real-user, agent) so distinct users never contend.