serve

package
v1.13.1 Latest Latest
Warning

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

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

Documentation

Overview

Package serve is the importable serve band: the config→listener composition that turns a loaded configuration into a running Harbor Protocol server.

Distinct from internal/server

internal/server is the Protocol server's request-handling core (scope authorizers, bootstrap handler, search-scope adapters). This package sits ABOVE it: it assembles the runtime (via internal/runtime/assemble), mounts the Protocol surfaces (via BuildMux), binds the listener, and owns the serve/close lifecycle. In short — internal/server answers requests; this package composes the process that listens for them.

One constructor, production-shaped by construction

Boot REQUIRES a non-nil auth-validator factory: identity is mandatory, so a nil factory is a loud construction error, never an unauthenticated listener. Boot mounts ONLY the surfaces every caller shares. Dev-only surfaces (the bootstrap-token endpoint, draft scaffolding, the token-rotate surface, the Console static build, fixture seeding, the mock-LLM snapshot override) are composed CALLER-SIDE through the injection seams on Options — the extra pre-CORS routes, the auth-surface builder, the LLM-snapshot builder, and the post-boot hook. The dev signer never reaches this package.

Lifecycle

Boot returns a Handle whose Serve binds the listener and runs until ctx cancels, and whose Close drains every subsystem in reverse dependency order. On any boot error every already-opened subsystem is Closed before returning.

cmd/harbor/session_ensurer.go — adapts the concrete sessions.Registry to the protocol.SessionEnsurer seam.

The Protocol ControlSurface owns the create-on-first-use behaviour on `start` but must not import the sessions package (it depends only on the error-only `protocol.SessionEnsurer` interface). This adapter lives at the cmd/harbor wiring boundary — the one place allowed to know both the concrete registry and the Protocol surface — and translates the registry's lifecycle sentinels into the protocol-side sentinels the surface's error mapper understands.

Index

Constants

This section is empty.

Variables

View Source
var ErrAuthValidatorFactoryRequired = errors.New("serve: auth-validator factory is required (identity is mandatory; nil is never an unauthenticated listener)")

ErrAuthValidatorFactoryRequired is returned by Boot when Options carries a nil AuthValidatorFactory. Identity is mandatory: the serve band never stands up an unauthenticated listener. Callers compare via errors.Is.

View Source
var ErrRunLoopDriverMisconfigured = errors.New("dev: per-task RunLoop driver missing a mandatory dependency")

ErrRunLoopDriverMisconfigured fires when NewRunLoopDriver is called with a nil bus / RunLoop / planner. Driver invariant: all three are mandatory.

Functions

func BootDeclaredMCPServerNames

func BootDeclaredMCPServerNames(cfg *config.Config) []string

BootDeclaredMCPServerNames returns the names of every MCP server declared in the boot yaml (`tools.mcp_servers[].name`) — the set the remove verb rejects and the run-start reconcile never detaches (boot-declared servers are not revisioned state).

func BootDeclaredMCPServerSet

func BootDeclaredMCPServerSet(cfg *config.Config) map[string]struct{}

BootDeclaredMCPServerSet returns the boot-declared MCP server names as a set for the run-start reconcile's O(1) skip check.

func InstanceID

func InstanceID(prefix string) string

InstanceID mints a stable-per-process instance identifier of the form "<prefix>-<hostname>" (bare prefix when the hostname is unavailable). A Console attached to multiple Runtimes keys each attachment by it. Shared by every production serve caller so the id shape stays uniform.

func MCPAddStdioAllowlist

func MCPAddStdioAllowlist(cfg *config.Config) []string

MCPAddStdioAllowlist projects the operator's stdio allowlist out of config for the runtime add-connection gate. A nil block yields a nil allowlist (fail-closed — every stdio add is rejected).

Types

type AuthSurfaceBuilder

type AuthSurfaceBuilder func(red audit.Redactor, bus events.EventBus, logger *slog.Logger) (*auth.RotateSurface, error)

AuthSurfaceBuilder builds the optional token-rotate surface after the runtime is assembled (it needs the redactor + bus the assembly produces). The dev caller injects a builder that closes over its dev signer; the production caller leaves it nil (no in-runtime token issuer).

type AuthValidatorFactory

type AuthValidatorFactory func(ctx context.Context, cfg *config.Config, red audit.Redactor, bus events.EventBus, logger *slog.Logger) (auth.Validator, error)

AuthValidatorFactory builds the Protocol auth validator from the loaded config plus the assembled redactor / bus / logger. The production caller injects a JWKS-backed factory; the dev caller injects one built from its ephemeral dev signer. It is REQUIRED — a nil factory fails Boot loud.

func NewJWKSAuthValidatorFactory

func NewJWKSAuthValidatorFactory() AuthValidatorFactory

NewJWKSAuthValidatorFactory returns THE production auth-validator factory: it projects the operator's identity config onto a JWKS-backed Validator (URL or file source), wiring the assembled redactor / bus / logger. The initial JWKS fetch runs synchronously inside the projection, so a bad source fails the boot loud. Every production serve caller (the serve subcommand and the external-serving facade) injects this one factory — a second hand-rolled copy is the drift this shared constructor exists to prevent.

type BuiltMux

type BuiltMux struct {
	// Mux is the transports mux the caller mounts under /v1/.
	Mux *http.ServeMux
	// FlowRegistry is the empty registry the flows surface serves. Nil when
	// the flows surface was not mounted (no artifact store / task registry).
	FlowRegistry *flow.Registry
}

BuiltMux is the result of BuildMux: the mounted Protocol mux plus the flow registry the flows surface serves (the caller seeds the same registry).

func BuildMux

func BuildMux(in MuxInput) (*BuiltMux, error)

BuildMux constructs every Protocol surface the non-nil handles in MuxInput imply and returns the mounted transports mux. It is the single source of the handle→transport-option mapping shared by cmd/harbor and the test kit.

type Enricher

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

Enricher is the production tasks.get Enricher for the dev stack. It provides parent-session / cost / planner-snapshot enrichment from in-memory runtime state, plus trajectory projection.

safe for concurrent reuse: the enricher is immutable after construction — the trajectory accessor is a pure function (no mutable receiver state).

func NewEnricher

func NewEnricher(trajectoryFn func(tasks.TaskID) *planner.Trajectory) *Enricher

NewEnricher builds the tasks.get Enricher over a trajectory accessor. The accessor is a pure function (the run-loop driver's TrajectoryByTaskID), so the returned Enricher is safe for concurrent reuse.

func (*Enricher) Cost

Cost returns a zero-valued cost rollup — cost aggregation is deferred to the `llm.cost.recorded` event stream.

func (*Enricher) ParentSession

ParentSession returns a zero-valued ref — the parent-session card is populated by the projector from the task identity when no enricher backfills it.

func (*Enricher) PlannerSnapshot

PlannerSnapshot returns nil — planner-checkpoint references are deferred to the checkpoint store.

func (*Enricher) Trajectory

Trajectory projects the planner's in-memory reasoning trace onto the Protocol wire. Steps with empty ReasoningTrace are filtered out. Returns nil when the task's trajectory is unavailable (evicted or the run-loop didn't store one).

type ExtraRoutesFunc

type ExtraRoutesFunc func(ctx context.Context, m RouteMount) ([]func(context.Context) error, error)

ExtraRoutesFunc mounts caller-side pre-CORS routes and returns any closers they registered. Called after the boot's validator and bind address are known and before the CORS wrap. On error, the closers accumulated up to the failing step MUST still be returned — Boot appends them to its rollback chain before inspecting the error, so a partially-mounted seam never leaks an already-constructed subsystem.

type Handle

type Handle struct {
	// Cfg / Bus are exported so a supervising caller (the dev hot-reload
	// loop) can read the reload policy and publish reload lifecycle events.
	Cfg *config.Config
	Bus events.EventBus
	// contains filtered or unexported fields
}

Handle is the running serve band a successful Boot returns. Serve binds the listener and runs until ctx cancels; Close drains every subsystem in reverse dependency order.

func Boot

func Boot(ctx context.Context, opts Options) (*Handle, error)

Boot reads the config, opens every subsystem, composes the Protocol surface, and returns a Handle whose Serve binds the listener. On any error every already-opened subsystem is Closed before returning.

func (*Handle) BindAddr

func (h *Handle) BindAddr() string

BindAddr reports the address the listener is (or will be) bound to. After Serve binds an ephemeral port it reflects the OS-assigned address. Internally synchronized — safe to call while Serve runs.

func (*Handle) Close

func (h *Handle) Close(ctx context.Context)

Close runs every subsystem's Close in reverse dependency order. Idempotent: the closer slice is drained exactly once; a second Close is a no-op.

func (*Handle) Handler

func (h *Handle) Handler() http.Handler

Handler returns the composed CORS-wrapped router the listener serves. It lets a caller drive the Protocol surface (and any caller-mounted pre-CORS route) through httptest without binding a port — the posture split between the dev and serve compositions is asserted this way.

func (*Handle) Serve

func (h *Handle) Serve(ctx context.Context) error

Serve binds the listener and runs the http.Server until ctx cancels.

type LLMSnapshotBuilder

type LLMSnapshotBuilder func(cfg *config.Config) (*llm.ConfigSnapshot, error)

LLMSnapshotBuilder projects the loaded config onto the LLM ConfigSnapshot the runtime opens the client with. The dev caller injects a builder that runs the mock-LLM gate and overrides the driver when the escape hatch fired; a nil builder uses the default llm.SnapshotFromConfig projection.

type MCPConnectionAttacher

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

MCPConnectionAttacher implements agentcfgprotocol.ConnectionAttacher by reusing the boot-time mcpdrv.Attach lifecycle (dial → initialize → discover → register) against the LIVE catalog + registry + bus. It owns a mutex-guarded closer chain so a runtime-added server's transport drains on stack teardown.

Concurrent reuse: the collaborators (catalog / registry / bus / logger / defaultIdentity) are set once at construction; the only mutable state is the closers slice, guarded by mu (documented internally-synchronised).

func NewMCPConnectionAttacher

func NewMCPConnectionAttacher(catalog tools.ToolCatalog, registry *mcpdrv.Registry, bus events.EventBus, logger *slog.Logger, defaultIdentity identity.Identity, oauthProviders map[string]toolauth.OAuthProvider) *MCPConnectionAttacher

NewMCPConnectionAttacher builds the production attacher. catalog, registry, and bus are mandatory (mcpdrv.Attach validates them too). oauthProviders may be nil when no provider is declared.

func (*MCPConnectionAttacher) Attach

Attach drives the real MCP attach for one runtime-added connection. The operator-supplied auth Headers flow to the live transport ONLY (the agent-config service never persists them). A partial-attach failure is drained by mcpdrv.Attach's own closer-chain handling; this method merges the per-add closers into its master chain under the lock so teardown drains the subprocess. An auth-required condition is surfaced by wrapping agentcfgprotocol.ErrAuthRequired so the service parks on the unified pause/resume primitive.

func (*MCPConnectionAttacher) Close

Close drains every runtime-added server's transport in reverse order. Wired into the boot closer chain so a runtime-added subprocess does not outlive the stack.

type MCPConnectionDetacher

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

MCPConnectionDetacher implements projection.ConnectionDetacher against the LIVE catalog + registry. Concurrent reuse: the collaborators are set once at construction; the type holds no mutable state (the registry + catalog own their own synchronisation).

func NewMCPConnectionDetacher

func NewMCPConnectionDetacher(catalog tools.ToolCatalog, registry *mcpdrv.Registry, logger *slog.Logger) *MCPConnectionDetacher

NewMCPConnectionDetacher builds the production detacher. catalog and registry are mandatory (the reconcile is a no-op without them).

func (*MCPConnectionDetacher) AttachedSources

func (d *MCPConnectionDetacher) AttachedSources(_ context.Context) []string

AttachedSources returns the source ids currently live in the MCP registry. NOTE: Registry.SourceIDs is a PROCESS-GLOBAL enumeration — correct for the single-agent dev wiring, but the future multi-agent attach leg must scope the attached set to the reconciling agent (see the projection.ConnectionDetacher interface doc).

func (*MCPConnectionDetacher) Detach

func (d *MCPConnectionDetacher) Detach(ctx context.Context, source string) error

Detach deregisters the source's tools from the catalog (when the catalog supports source deregistration — the optional CatalogSourceDeregisterer companion) and from the MCP registry, closing its transport gracefully. An already-gone source is a no-op (ErrServerNotFound is swallowed — idempotent).

type MuxInput

type MuxInput struct {
	Cfg     *config.Config
	Surface *protocol.ControlSurface
	Bus     events.EventBus
	// Redactor / Logger / Metrics feed every surface's audit + posture legs.
	Redactor audit.Redactor
	Logger   *slog.Logger
	Metrics  *telemetry.MetricsRegistry
	// LLMSnapshot is the resolved snapshot the posture provider projects.
	LLMSnapshot llm.ConfigSnapshot

	// Core subsystem handles.
	Tasks          tasks.TaskRegistry
	Sessions       *sessions.Registry
	Agents         *agentregistry.Registry
	Artifacts      artifacts.ArtifactStore
	Memory         memory.MemoryStore
	Catalog        tools.ToolCatalog
	Coordinator    pauseresume.Coordinator
	MCPRegistry    *mcpdrv.Registry
	MCPToolContext *mcpconsole.ToolContextStore
	State          state.StateStore
	Skills         skills.SkillStore

	// Control-plane handles.
	AgentConfig    agentcfg.Registry
	AgentConfigID  string
	SessionOverlay sessionoverlay.Store
	RunsStore      *runsprotocol.Store
	// RunLoopDriver backs the tasks.get trajectory enricher. Nil leaves
	// tasks reads un-enriched (a stack without a run loop).
	RunLoopDriver  *RunLoopDriver
	OAuthProviders map[string]toolauth.OAuthProvider

	// Governance handles. TenantOverridePolicy backs governance.set/get
	// tenant overrides; KeyRotator backs governance.rotate_key. Both are
	// built caller-side (the policy is shared with the run-loop driver; the
	// rotator's lifecycle is the assembly's).
	TenantOverridePolicy *governance.TenantOverridePolicy
	KeyRotator           *llm.ProviderKeyRotator
	ValidModels          []string

	// MCPAttacher backs agent_config.add_mcp_connection. Built caller-side so
	// its Close joins the caller's closer chain; nil leaves the add verb
	// unwired.
	MCPAttacher       agentcfgprotocol.ConnectionAttacher
	MCPStdioAllowlist []string
	BootDeclaredMCP   []string

	// Auth. Validator nil mounts the transports WithoutValidator (the
	// explicit test-kit opt-out); AuthSurface nil leaves auth.rotate_token
	// un-mounted (the production posture — no in-runtime token issuer).
	Validator   auth.Validator
	AuthSurface *auth.RotateSurface

	// Posture stamps.
	DisplayName  string
	InstanceID   string
	BuildVersion string
	BuildCommit  string
	// TopologyAvailable advertises the topology.snapshot capability (true
	// only when the caller wired a topology accessor onto the surface).
	TopologyAvailable bool
}

MuxInput carries the assembled subsystem handles and the caller-side posture stamps BuildMux mounts the Protocol surface over. Every handle is optional except the ControlSurface and the bus: a nil handle leaves the corresponding surface un-mounted (the route then 404s), exactly as the two callers already behave under their skip/absence paths.

type Options

type Options struct {
	ConfigPath string

	// Config, when non-nil, is used directly instead of loading and
	// validating ConfigPath — the entry point for a caller that already
	// holds a validated (or programmatically-built) configuration.
	// Boot re-runs the full-binary Validate on it so a hand-built config
	// cannot bypass validation. When both Config and ConfigPath are set
	// Config wins; when Config is nil ConfigPath is loaded.
	Config *config.Config

	Port            int
	BindAddr        string
	Logger          *slog.Logger
	Stderr          io.Writer
	SubcommandLabel string

	// PreferConfigBindAddr opts into honoring the operator-configured
	// `server.bind_addr` (which may name a non-loopback interface) when no
	// explicit BindAddr override is given. ONLY the production serve caller
	// sets it. The dev/console callers leave it false so they stay
	// loopback-only on 127.0.0.1:<Port> — a dev boot against a serve-shaped
	// yaml must never expose the dev-token stack off-box.
	PreferConfigBindAddr bool

	// AuthValidatorFactory is REQUIRED (nil fails Boot loud).
	AuthValidatorFactory AuthValidatorFactory

	// MCPDefaultIdentity is the identity a boot-time / runtime-added MCP
	// connection dials under.
	MCPDefaultIdentity identity.Identity

	// DisplayName / InstanceID stamp the posture surface's runtime.info.
	DisplayName string
	InstanceID  string
	// BuildVersion / BuildCommit stamp runtime.info's build identity.
	BuildVersion string
	BuildCommit  string
	// DevAllowMock stamps the Serve start log's dev_allow_mock attribute —
	// the dev caller sets it when the mock escape hatch fired so an operator
	// reading the boot line sees the dev-only posture. A stamp only; the mock
	// gate itself is caller policy inside BuildLLMSnapshot.
	DevAllowMock bool

	// RegisterCatalog forwards a compiled-tool registrar onto the
	// assembly's pre-policy catalog seam (assemble.Options.RegisterCatalog):
	// a tool it registers is wrapped with its declared approval / OAuth /
	// policy shell before the run loop can dispatch it. Nil is a no-op —
	// the stock serve caller passes nil; an external serving binary passes
	// its agent's RegisterTools.
	RegisterCatalog func(catalog tools.ToolCatalog) error

	// Caller-side injection seams (all optional).
	BuildLLMSnapshot LLMSnapshotBuilder
	BuildAuthSurface AuthSurfaceBuilder
	ExtraRoutes      ExtraRoutesFunc
	PostBoot         PostBootFunc
}

Options bundles the inputs Boot consumes. ConfigPath (not a pre-loaded config) is carried so a hot-reload supervisor re-reads the file on each reboot by re-calling Boot with the same Options.

type PostBootFunc

type PostBootFunc func(ctx context.Context, h PostBootHandles) error

PostBootFunc runs after the listener + surfaces are composed but before Serve. The dev caller uses it to seed fixture entities.

type PostBootHandles

type PostBootHandles struct {
	Sessions  *sessions.Registry
	Agents    *agentregistry.Registry
	Tasks     tasks.TaskRegistry
	Artifacts artifacts.ArtifactStore
	Memory    memory.MemoryStore
	Tools     tools.ToolCatalog
	Flows     *flow.Registry
	Bus       events.EventBus
	Logger    *slog.Logger
}

PostBootHandles carries the assembled subsystem handles the post-boot hook receives (the dev caller's fixture seeder writes through them).

type RouteMount

type RouteMount struct {
	Router    *http.ServeMux
	Validator auth.Validator
	Bus       events.EventBus
	Redactor  audit.Redactor
	Logger    *slog.Logger
	// BindAddr is the resolved listen address (the bootstrap handler puts it
	// in its connection envelope).
	BindAddr string
}

RouteMount is handed to the ExtraRoutes seam so a caller can mount pre-CORS routes (draft scaffolding, the bootstrap-token endpoint, the Console static build) with access to the boot's validator, bus, and bound address. The caller returns any closers its routes registered (a draft store's Close).

type RunLoopDriver

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

RunLoopDriver subscribes to `task.spawned` and drives a RunLoop per spawned foreground task. The driver is constructed by bootDevStack and Closed during stack teardown.

func NewRunLoopDriver

func NewRunLoopDriver(opts RunLoopDriverOptions) (*RunLoopDriver, error)

NewRunLoopDriver validates the opts and returns a stopped driver. Call Start before serving; call Close to drain.

func (*RunLoopDriver) Close

func (d *RunLoopDriver) Close(_ context.Context) error

Close cancels the subscription, waits for the subscribe-loop to drain, then waits for every in-flight RunLoop goroutine to return. Idempotent: a second Close walks no-ops. The supplied ctx is accepted for the closer-signature compatibility (closeFns takes a ctx); the driver's drain has its own bounded shape (every RunLoop observes d.subCtx cancellation and returns within one drain boundary). A pathological RunLoop that holds ctx-cancellation indefinitely would block Close; the dev cmd's serve loop applies the Server.ShutdownGracePeriod ceiling at the http boundary, so a blocked Close eventually surfaces as a graceless exit.

func (*RunLoopDriver) SessionOverridesStore

func (d *RunLoopDriver) SessionOverridesStore() *runsprotocol.Store

SessionOverridesStore returns the pending-override Store the driver consumes at run start. It is the seam a caller wires the runs.set_overrides service to so a set reaches the run; exposed so a caller can assert the two share one Store.

func (*RunLoopDriver) Start

func (d *RunLoopDriver) Start(ctx context.Context) error

Start opens the admin-scoped subscription and launches the subscribe-loop goroutine. Idempotent: a second Start is a no-op. The supplied ctx anchors the subscription's lifetime — when ctx cancels (e.g. boot was aborted before Close), the subscription cancels along with it.

func (*RunLoopDriver) TrajectoryByTaskID

func (d *RunLoopDriver) TrajectoryByTaskID(taskID tasks.TaskID) *planner.Trajectory

TrajectoryByTaskID returns a defensive SNAPSHOT of a run's trajectory (its Query + a copy of the append-only Steps slice), or nil when the task's trajectory has been evicted or never existed. Safe for an out-of-band Protocol reader (the Enricher on a tasks.get) to call while the run is IN-FLIGHT: the snapshot is taken under the run's per-run trajMu — the SAME lock the steering RunLoop holds around each per-step append — so the returned Steps slice never observes a mid-append slice header (the data race the Enricher would otherwise hit against the run loop). Only the fields the Enricher reads (Query + Steps) are copied; the trajectory's shared maps are deliberately NOT aliased into the snapshot.

type RunLoopDriverOptions

type RunLoopDriverOptions struct {
	Logger   *slog.Logger
	Bus      events.EventBus
	RunLoop  *steering.RunLoop
	Planner  planner.Planner
	Tasks    tasks.TaskRegistry // mandatory: the FSM the driver advances on Run exit
	TaskKind tasks.TaskKind     // KindForeground at V1; the driver spawns RunLoops for this kind

	// driveBackground widens the driver to ALSO
	// drive KindBackground tasks — the ones a planner-emitted SpawnTask
	// creates. False (the default / legacy test path) keeps the
	// foreground-only behaviour. Recursion is bounded at the spawn site
	// by the dev executor's absolute_max_spawn_depth cap, not here.
	DriveBackground bool

	// RunContext consumer wiring. All three of
	// memory / skillsDirectory / planningHints are OPTIONAL: a dev
	// stack that did not open the respective subsystem hands nil; the
	// driver projects the corresponding RunContext field to nil and
	// the planner omits the wrapper. The skills
	// surface is the `skills.Directory` — the bounded,
	// pinned-then-recent, capability-filtered `<skills_context>`
	// producer (the directory carries its own MaxEntries cap; the
	// pre-111d SkillStore.Search + skillsContextMax pair is deleted).
	Memory          memory.MemoryStore
	MemoryRecall    memory.RecallSettings
	SkillsDirectory *skills.Directory
	PlanningHints   *planner.PlanningHints

	// tool dispatch + Catalog projection +
	// Trajectory. The tool catalog is the shared catalog the rest of
	// the dev stack already populated (in-process tools, MCP-discovered
	// tools, etc.). MaxStepsRunLoop caps the runloop's outer step
	// counter (separate from the planner-internal cap that goes onto
	// react via PlannerConfig.MaxSteps).
	Catalog         tools.ToolCatalog
	Executor        steering.ToolExecutor
	MaxStepsRunLoop int

	// operator-declared GrantedScopes
	// threaded into the per-run catalog view's CatalogFilter. Tools
	// whose AuthScopes exceed this set are invisible to the planner.
	// Nil / empty list means no scopes granted (the existing latent
	// default before the plumb-through).
	GrantedScopes []string

	// the artifact store the multimodal
	// materializer reads from. Required only when `task.InputArtifactIDs`
	// is non-empty (text-only tasks never touch the store). A nil
	// store with input artifacts on the task degrades gracefully —
	// the materializer emits text-stub-only references the LLM
	// routes via the catalog.
	ArtifactStore artifacts.ArtifactStore

	// trajectory compression. `tokenBudget`
	// projects onto RunSpec.Base.Budget.TokenBudget (the per-run
	// runtime budget — a run option, never
	// planner state); `compression` is the assembly-built
	// planner.CompressionRunner the runloop invokes at each step
	// boundary when the budget is non-zero. Both zero/nil (the
	// default) = compression off, byte-identical behaviour.
	TokenBudget int
	Compression *planner.CompressionRunner

	// the per-agent attachment disposition policy
	// decoded from `multimodal.disposition` (the middle precedence
	// layer of the disposition resolution). Zero value = no agent
	// policy; the runtime default applies.
	DispositionPolicy planner.DispositionPolicy

	// tenantOverrides resolves an admin-set tenant default for the run's
	// LLM parameters at run start (model / extra-instructions /
	// temperature / max-tokens / reasoning-effort). OPTIONAL — a nil
	// resolver means "no tenant defaults to apply" (the run uses the
	// agent/config defaults). When supplied, the driver reads it ONCE per
	// run and pins the resolved snapshot into the run's RunContext so the
	// swap lands on this run (next-turn relative to the admin's set), never
	// mid-flight.
	TenantOverrides TenantOverrideResolver

	// sessionOverrides is the in-process pending-override Store the
	// `runs.set_overrides` Service writes into. The driver Consumes the
	// session's pending override at run start (one-shot) and composes it
	// OVER the tenant default (session › tenant › config). OPTIONAL — a
	// nil Store means "no session overrides" (the run uses tenant/config
	// only). It is the SAME Store handed to the runs Service so a set and
	// the consume meet.
	SessionOverrides *runsprotocol.Store

	// agentConfig resolves the agent's active config revision at run start
	// (the agent-config control plane). OPTIONAL — a nil registry means
	// "no agent-config projection" (the run uses every directory-visible
	// skill). When supplied with a non-empty agentConfigID, the run reads
	// the active revision ONCE at run start and pins the resolved
	// skills-set into the per-run projection so a config edit applies on
	// the NEXT run (next-turn-only), never mid-flight.
	AgentConfig   agentcfg.Registry
	AgentConfigID string

	// sessionOverlay resolves the SESSION-scoped safe-subset overlay (the
	// non-admin lower tier) at run start: the session's user prompt layer,
	// narrow-only source/tool disables (UNIONED into the admin exclusion set
	// — can only narrow, never widen), and ephemeral personal skills. Keyed
	// by the REAL (tenant, user, session) triple, so it is session-isolated.
	// OPTIONAL — nil means "no session overlay" (the run uses the admin agent
	// config only). The SAME store the session-safe Protocol verbs write into.
	SessionOverlay sessionoverlay.Store

	// runCompletionHook is the static `runtime.hooks.run_completion`
	// projection (nil when unset). At run start the driver resolves the
	// effective hook via the shared projection (agent-config over this yaml
	// over none) and pins it into the RunSpec, so an edit lands next-run.
	RunCompletionHook *steering.CompletionHookSpec

	// connectionDetacher drives the DETACH leg of run-start reconciliation:
	// at run start the driver detaches every MCP server that is attached but
	// no longer declared by the agent's active config revision (a removed
	// connection, or a rollback past an add), so the next run's projected
	// catalog excludes it. OPTIONAL — nil means no reconcile (the
	// backward-compatible path). Injected at the cmd/harbor boundary (it
	// imports the concrete MCP driver; this driver stays driver-agnostic).
	ConnectionDetacher projection.ConnectionDetacher

	// bootDeclaredMCP is the set of boot-declared (yaml) MCP server names the
	// reconcile MUST NEVER detach (they are not revisioned state). Nil/empty
	// when no yaml server is declared.
	BootDeclaredMCP map[string]struct{}

	// namingDefault is the static `runtime.naming` fleet-default auto-naming
	// policy; the driver resolves the effective policy per run (agent-config
	// over this yaml over off). Opt-in, default off.
	NamingDefault config.RuntimeNamingConfig

	// sessionTitler is the session-registry seam the auto-naming trigger
	// writes/reads through (RecordCompletedTurn / AutoNamingState /
	// SetTitleAuto). The same *sessions.Registry the sessions Protocol routes
	// project over. Nil ⇒ no auto-naming trigger even when a policy resolves.
	SessionTitler steering.SessionTitler

	// namingLLM is the run's wrapped LLM client the ONE naming Complete call
	// flows through (governance/safety via ctx identity). Nil ⇒ no auto-naming
	// trigger.
	NamingLLM steering.NamingCompleter
}

RunLoopDriverOptions bundles the dependencies the driver consumes. Bus + RunLoop + Planner + TaskRegistry are all mandatory; a nil any of them returns ErrRunLoopDriverMisconfigured from NewRunLoopDriver. The TaskRegistry is what the driver calls MarkRunning / MarkComplete / MarkFailed on to advance the FSM (closes issue #123).

type SessionEnsurerAdapter

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

SessionEnsurerAdapter wraps a *sessions.Registry as a protocol.SessionEnsurer. Immutable after construction; the wrapped registry is itself concurrency-safe, so the adapter is too.

func NewSessionEnsurerAdapter

func NewSessionEnsurerAdapter(reg *sessions.Registry) *SessionEnsurerAdapter

NewSessionEnsurerAdapter builds the adapter. A nil registry would be a wiring bug; the caller (cmd_dev bootDevStack) always passes the constructed registry.

func (*SessionEnsurerAdapter) EnsureSession

func (a *SessionEnsurerAdapter) EnsureSession(ctx context.Context, ident identity.Identity) error

EnsureSession implements protocol.SessionEnsurer. It calls the registry's create-on-first-use EnsureOpen and translates the registry's sentinels into the protocol-side sentinels so the ControlSurface's mapSessionEnsureError reaches a stable Protocol code. An unclassified error is wrapped (not swallowed) so the surface's catch-all maps it to CodeRuntimeError (CLAUDE.md §5 — fail loud).

type TenantOverrideResolver

type TenantOverrideResolver interface {
	Get(ctx context.Context, tenant string) (governance.TenantOverrideSpec, bool, error)
}

TenantOverrideResolver is the narrow read seam the run loop uses to resolve an admin-set tenant default at run start. The `*governance.TenantOverridePolicy` concrete satisfies it.

Directories

Path Synopsis
Package external is the production-only entry point behind the public sdk/server facade: it turns a validated configuration into a running Harbor Protocol server that an external Go binary can host, with its compiled in-process tools wrapped at the pre-policy catalog seam.
Package external is the production-only entry point behind the public sdk/server facade: it turns a validated configuration into a running Harbor Protocol server that an external Go binary can host, with its compiled in-process tools wrapped at the pre-policy catalog seam.

Jump to

Keyboard shortcuts

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