Documentation
¶
Overview ¶
Package virtualagent owns Harbor's canonical virtual-agent profile representation: the bounded, non-recursive overlay a configured top-level agent applies to a planner-spawned child run, the immutable profile map frozen at the parent's run start, and the pinned binding persisted on the child task so a restart reproduces the exact profile.
What a virtual agent is ¶
A virtual agent is NOT a registered agent. It never appears in `agents.list`, is never a `control.start` target, and its key never joins the isolation tuple. `Task.AgentID` stays the owning top-level agent; the profile is a per-run CONFIGURATION projection only, exactly like the agent-config control plane's other sections.
The canonical representation (YAML and AgentConfig revisions) ¶
A profile is declared either in the boot YAML (`virtual_agents:`) or in an immutable AgentConfig revision's `virtual_agents` section. Both doors decode into the SAME canonical Profile / Overlay shapes in this package, normalized by the SAME functions, and hashed by the SAME Profile.Hash — a YAML-declared profile and a revision-pinned profile with identical content are byte-identical in canonical form.
The overlay is bounded and non-recursive ¶
An overlay MAY narrow the child's model parameters, skills (intersection), tool exposure (union into the exclusion set) and limits (max-steps / token-budget), and MAY add trusted specialist instructions. It CANNOT widen resources or guardrails, attach capabilities, own providers / hooks / memory, recurse into another profile, or target A2A — those fields do not exist in Overlay, so no value can express them (structurally impossible, not merely validated). Omission is byte-compatible: a run that never selects a profile behaves exactly as before.
Freeze, bind, pin ¶
The parent's run start resolves the effective profile map (revision section over YAML), validates it, and FREEZES it against the parent's active config revision id + digest. A planner spawn selecting a profile validates key-unknown / overlay-invalid / stale-live-revision BEFORE anything persists; the accepted spawn carries a Binding (the owning agent, key, label, parent, config revision id + digest, profile hash) onto the task record. The child's run start re-resolves the current map and validates the pin: a moved config revision, an edited profile definition, or a tampered binding fails the child run LOUD — a restart reproduces the exact profile or does not run at all. A virtual-profile run cannot itself spawn a virtual profile (non-recursive).
Index ¶
- Constants
- Variables
- func BlockName(k Key) string
- func ClampMax(v int, parent *int) int
- func IntersectStrings(base, keep []string) []string
- func OverlayClampMaxSteps(parentCap int, overlay *int) int
- func OverlayClampMaxTokens(parentResolved *int, overlay *int) *int
- func OverlayClampTokenBudget(parent int, overlay *int) int
- func UnionStrings(a, b []string) []string
- func ValidateBinding(b Binding) error
- func ValidateKey(k Key) error
- func ValidateMap(m Map) error
- func ValidateOverlay(o Overlay) error
- func ValidateProfile(p Profile) error
- func WithFrozenMap(ctx context.Context, f *FrozenMap) context.Context
- func WithRunBinding(ctx context.Context, b *Binding) context.Context
- type Binding
- type FrozenMap
- type Key
- type LiveRevisionReader
- type Map
- type Overlay
- type Profile
Constants ¶
const ( // MaxKeyLength bounds a profile key in bytes. MaxKeyLength = 128 // MaxLabelLength bounds a profile label in bytes. MaxLabelLength = 200 // MaxModelLength bounds the overlay model name in bytes. MaxModelLength = 128 // MaxSkillEntries bounds the overlay skill-narrowing set size. MaxSkillEntries = 100 // MaxToolListEntries bounds each overlay tool-exclusion list size. MaxToolListEntries = 500 // MaxInstructionsBytes bounds the additive specialist-instruction // text (trusted, operator-authored — bounded, never unbounded). MaxInstructionsBytes = 16 * 1024 // MaxMaxTokens bounds the overlay max-tokens value. MaxMaxTokens = 1_000_000 // MaxMaxSteps bounds the overlay max-steps value. MaxMaxSteps = 100_000 // MaxMaxTokenBudget bounds the overlay token-budget value. MaxMaxTokenBudget = 10_000_000 // DefaultMaxProfiles bounds the operator-owned virtual profile map. DefaultMaxProfiles = 32 )
Size bounds. Single-sourced here; the config validator and the agentcfg normalization call into this package rather than carrying literal copies.
Variables ¶
var ( // ErrInvalidKey — a profile key is empty, over-long, or outside the // restricted identifier charset. ErrInvalidKey = errors.New("virtualagent: invalid profile key") // ErrInvalidOverlay — the overlay violates a bounded narrow-only // invariant (an unknown dimension is structurally impossible; this // covers bounds / charset / narrowing-shape violations). ErrInvalidOverlay = errors.New("virtualagent: invalid overlay") // ErrInvalidProfile — the profile envelope (key / label / parent / // overlay) failed validation. ErrInvalidProfile = errors.New("virtualagent: invalid profile") // ErrInvalidMap — the profile map failed validation (owner mismatch, // duplicate key, an invalid member). ErrInvalidMap = errors.New("virtualagent: invalid profile map") // ErrUnknown — a spawn selected a profile key absent from the frozen // map. Fails BEFORE persistence. ErrUnknown = errors.New("virtualagent: unknown profile key") // ErrInvalid — a spawn selected a profile whose overlay failed // re-validation at the dispatch boundary. Fails BEFORE persistence. ErrInvalid = errors.New("virtualagent: invalid selected profile") // ErrStale — the selected profile is stale: the live parent-config // revision no longer matches the frozen map's pinned revision (at // spawn), or the persisted binding no longer matches the current // revision / profile definition (at child run start). Fails before // persistence at spawn; fails the child run LOUD at run start. ErrStale = errors.New("virtualagent: stale profile pin") // ErrRecursion — a run that IS a virtual-profile run tried to spawn // another virtual-profile child. The overlay is non-recursive. ErrRecursion = errors.New("virtualagent: virtual profile recursion") // ErrNoMap — a spawn selected a profile in a run whose effective // agent is not the profile-owning top-level agent (no frozen map). ErrNoMap = errors.New("virtualagent: no virtual-agent profile map in this run") // ErrTampered — the persisted binding disagrees with the resolved // profile (label / parent / hash mismatch). Fails the child run LOUD. ErrTampered = errors.New("virtualagent: tampered profile binding") // ErrMissing — the persisted binding's key is absent from the current // profile map. Fails the child run LOUD. ErrMissing = errors.New("virtualagent: bound profile key is missing from the current map") )
Sentinel errors. Callers compare via errors.Is.
Functions ¶
func BlockName ¶
BlockName returns the ExtraSystemBlocks block name a profile's specialist instructions render under: `virtual_agent.<key>`. The name is stable and key-unique so a transcript reader can attribute the block, and a future profile edit that removes the block drops it.
func ClampMax ¶
ClampMax returns v clamped to at most parent. When parent is nil (the parent resolved no ceiling), v is returned unchanged. Used for the never-widen limits (max-tokens / max-steps / token-budget).
func IntersectStrings ¶
IntersectStrings returns the sorted intersection of base and keep — the narrow-only skills operation. A nil/empty keep returns nil (no narrowing).
func OverlayClampMaxSteps ¶
OverlayClampMaxSteps clamps an overlay max-steps against the parent driver's run-loop cap. The overlay may only ever tighten the cap.
func OverlayClampMaxTokens ¶
OverlayClampMaxTokens clamps an overlay max-tokens against the parent's RESOLVED effective ceiling. When the parent resolved none (nil), the overlay value is used as-is; when the overlay is nil, the parent's value is preserved.
func OverlayClampTokenBudget ¶
OverlayClampTokenBudget clamps an overlay token budget against the parent's resolved budget. A zero / nil parent budget means "no budget" (compression off); an overlay budget then introduces a limit (a narrowing), and a parent budget caps the overlay at it.
func UnionStrings ¶
UnionStrings returns the sorted union of two exclusion sets — the narrow-only tools operation (it can only hide more).
func ValidateBinding ¶
ValidateBinding checks the structural invariants of a persisted binding (all fields non-empty + bounded; the digest is a 64-hex SHA-256). Callers additionally verify the binding against the frozen / current map — this is the shape check only.
func ValidateKey ¶
ValidateKey checks the key charset + length bounds.
func ValidateMap ¶
ValidateMap checks the owner + every member's parent-owner binding.
func ValidateOverlay ¶
ValidateOverlay checks the bounded narrow-only invariants. Unknown dimensions are structurally unrepresentable; this validates bounds / charset / reasoning-effort values and the narrowing shape (list members bounded, never an enable set).
func ValidateProfile ¶
ValidateProfile checks the profile envelope (key / label / parent / overlay bounds).
func WithFrozenMap ¶
WithFrozenMap attaches the parent run's frozen profile map to ctx so the dispatch executor can validate planner spawn selectors against it. Per-run state in ctx — never on a shared artifact.
func WithRunBinding ¶
WithRunBinding attaches the CURRENT run's own profile binding to ctx so the dispatch executor can enforce the non-recursion rule (a virtual-profile run cannot spawn another virtual profile). Attached only when the run IS a virtual-profile run.
Types ¶
type Binding ¶
type Binding struct {
AgentID string `json:"agent_id,omitempty"`
Key Key `json:"key,omitempty"`
Label string `json:"label,omitempty"`
Parent string `json:"parent,omitempty"`
ConfigRevisionID string `json:"config_revision_id,omitempty"`
ConfigDigest string `json:"config_digest,omitempty"`
ProfileHash string `json:"profile_hash,omitempty"`
// Profile is the sealed canonical snapshot used to reconstruct the child
// without consulting mutable configuration after admission.
Profile Profile `json:"profile"`
}
Binding is the immutable per-task metadata that pins a virtual-agent profile: persisted on the child task at spawn (before the child runs) and re-validated at the child's run start so a restart reproduces the exact profile. `AgentID` is the owning TOP-LEVEL agent — it stays `Task.AgentID`; the binding never re-keys the task to a different agent and never joins the isolation tuple.
func CloneBinding ¶
CloneBinding returns a defensive copy suitable for crossing a persistence or task boundary. The snapshot's pointer and slice fields never alias the caller's binding.
func RunBindingFrom ¶
RunBindingFrom returns the current run's own profile binding (nil when the run is not a virtual-profile run).
type FrozenMap ¶
type FrozenMap struct {
Owner string
RevisionID string
ConfigDigest string
// contains filtered or unexported fields
}
FrozenMap is the profile map frozen at the parent's run start: the canonical Map bound to the parent agent's active config revision id + digest. A spawn validates against the frozen map (unknown / invalid / stale), and the persisted Binding pins the frozen revision + digest + per-profile hash so the child's run start reproduces the exact profile — or fails loud.
A FrozenMap is per-run state carried on the run's ctx (the concurrent-reuse contract): never a field on a shared artifact.
func FrozenMapFrom ¶
FrozenMapFrom returns the frozen map attached to ctx (nil when absent).
func NewFrozenMap ¶
func NewFrozenMap(m Map, revisionID, configDigest string, live LiveRevisionReader) (*FrozenMap, error)
NewFrozenMap freezes a validated canonical map against a parent-config revision. A nil map or an empty owner returns ErrInvalidMap. live is the optional staleness probe (nil disables the live re-check).
func (*FrozenMap) Bind ¶
Bind constructs the binding for the frozen profile p under the map's owner: agent = parent = owner (the top-level agent), pinned to the frozen revision + digest + the profile's hash.
func (*FrozenMap) VerifyCurrent ¶
VerifyCurrent is the spawn-time staleness probe: it re-reads the live parent-config revision and returns ErrStale when it no longer matches the frozen pin. A nil live reader (the default in pure unit tests) trusts the frozen pin and returns nil.
func (*FrozenMap) VerifyPin ¶
VerifyPin is the child-run-start pin check: it validates a persisted binding against the CURRENT frozen map so a restart reproduces the exact profile. It returns:
- ErrMissing when the bound key is absent from the current map,
- ErrStale when the current parent-config revision/digest differs from the binding's pin,
- ErrTampered when the current profile's hash (or label / parent) disagrees with the binding.
On nil error, the returned profile is the exact profile the child must run under.
type LiveRevisionReader ¶
LiveRevisionReader is the per-run injected seam the frozen map uses to re-read the parent agent's CURRENT active config revision at spawn time (the staleness probe). The run-loop driver builds it over its agentcfg registry + the run's identity triple; it carries no identity because the driver already captured it. A nil function is a no-op stale check (the frozen map's pinned revision is trusted as-is).
type Map ¶
type Map struct {
Owner string `json:"owner,omitempty"`
MaxProfiles int `json:"max_profiles,omitempty"`
Profiles []Profile `json:"profiles,omitempty"`
}
Map is the canonical profile map owned by ONE configured top-level agent. The map's owner must equal the runtime's configured top-level agent id; every profile's Parent must equal the owner.
func NormalizeMap ¶
NormalizeMap returns the canonical map form: profiles sorted by key (key-unique; the LAST duplicate wins, matching the agentcfg connection-section convention).
type Overlay ¶
type Overlay struct {
Model *string `json:"model,omitempty"`
Temperature *float64 `json:"temperature,omitempty"`
MaxTokens *int `json:"max_tokens,omitempty"`
ReasoningEffort *string `json:"reasoning_effort,omitempty"`
// Skills is a pointer so an EXPLICIT empty set (narrow to no skills)
// is distinguishable from omission (no narrowing). nil = no skill
// narrowing.
Skills *[]string `json:"skills,omitempty"`
DisabledTools []string `json:"disabled_tools,omitempty"`
PausedServers []string `json:"paused_servers,omitempty"`
MaxSteps *int `json:"max_steps,omitempty"`
TokenBudget *int `json:"token_budget,omitempty"`
Instructions string `json:"instructions,omitempty"`
}
Overlay is the explicit bounded non-recursive overlay a profile applies over its parent's frozen configuration. Every dimension is narrow-only or additive:
- Model / Temperature / MaxTokens / ReasoningEffort narrow the child's sampling parameters (the child's max-tokens ceiling is clamped to the parent's resolved ceiling — see OverlayClampMaxTokens).
- Skills is an INTERSECTION: the child keeps only parent skills named here (nil = no narrowing).
- DisabledTools / PausedServers are UNIONED into the exclusion set: they can only hide tools the parent exposed, never re-expose one.
- MaxSteps / TokenBudget narrow the child's run limits (clamped to the parent's own cap).
- Instructions adds trusted, operator-authored specialist guidance (rendered verbatim in the trusted additive position, bounded).
There is deliberately NO field for providers, hooks, memory, capabilities, a parent-profile reference, or an A2A target: those dimensions cannot be expressed, so they can never be widened, attached, or recursed into.
func NormalizeOverlay ¶
NormalizeOverlay returns the canonical form: sorted / de-duplicated list members, trimmed instructions, preserved pointer presence. A nil or empty-nil list normalises to nil so omission is byte-compatible.
type Profile ¶
type Profile struct {
Key Key `json:"key"`
Label string `json:"label,omitempty"`
Parent string `json:"parent,omitempty"`
Overlay Overlay `json:"overlay,omitempty"`
// InputPatterns bounds which inherited artifact references a child may
// receive. Matching is against the canonical filename and MIME type;
// the model supplies IDs, never content or a URL.
InputPatterns []string `json:"input_patterns,omitempty"`
// InputCount is the maximum number of inherited input references.
InputCount int `json:"input_count,omitempty"`
// InputDisposition is the configuration-owned disposition applied to
// every accepted input reference. Empty preserves the normal policy.
InputDisposition string `json:"input_disposition,omitempty"`
// OutputSchema is the configuration-owned terminal contract. Its hash is
// pinned in the profile binding and is never accepted from the model.
OutputSchema json.RawMessage `json:"output_schema,omitempty"`
OutputSchemaHash string `json:"output_schema_hash,omitempty"`
}
Profile is the canonical virtual-agent profile shared by the boot YAML declaration and immutable AgentConfig revisions.
func NormalizeProfile ¶
NormalizeProfile returns the canonical profile form.