Documentation
¶
Overview ¶
Package capability owns the assistant's capability-document schema and the composition that turns a project's documents into the model's system-prompt knowledge and provider tools.
This is the contract inversion made concrete: the assistant, not the control plane, owns the capability-document type. The on-the-wire JSON shape is still the services.miloapis.com/v1alpha1 AgentBinding projection (so existing fixtures parse unchanged), but the Go types here are assistant-native and carry no control-plane client. A "capability provider" publishes documents; how they reach the assistant is a [CapabilitySource] concern (fixture file today, an HTTP source later).
Index ¶
- Constants
- func GapReportToolName(serviceRefName string) string
- func NamespaceToolName(serverName, toolName string) string
- func SanitizeName(v string) string
- type Authority
- type AuthorityRead
- type CallerIdentity
- type CapabilityDocument
- type CapabilitySpec
- type ComposeOptions
- type Composed
- type Condition
- type FixtureSource
- type GVKRef
- type HTTPSource
- type Knowledge
- type KnowledgeConcept
- type KnowledgeSource
- type KnowledgeSourceType
- type MCPServer
- type Metadata
- type ProviderToolInvocation
- type Ref
- type ServiceEntitlement
- type Skill
- type Source
- type Status
- type ToolSelector
- type Tools
Constants ¶
const ( // DefaultMCPConnectTimeout bounds a single MCP server connect. DefaultMCPConnectTimeout = 5 * time.Second // ToolNamespaceSeparator joins the server shortname and the provider tool // name into the model-facing tool name. ToolNamespaceSeparator = "__" )
Tool composition defaults (Tier 2).
const ( // AuthorizationHeader carries the caller's own bearer token. AuthorizationHeader = "Authorization" // ProjectHeader names the project the provider must read in. It comes from // the authorized request, never from a model-supplied tool argument. ProjectHeader = "X-Datum-Project" )
Caller-identity propagation to provider MCP servers, so one can read the customer's own resources as the caller. See docs/architecture/identity-and-access.md.
A capability document is provider-controlled data, so naming an endpoint grants it nothing: identity travels only to hosts an OPERATOR sanctioned (ComposeOptions.IdentityForwardHosts), empty by default. Knowledge sources and skill bodies are excluded outright — plain GETs of URLs named by that same document, so a credential there would only widen the harvest surface.
const ( // DefaultKnowledgeTimeout bounds each source fetch (connect + body read). DefaultKnowledgeTimeout = 3 * time.Second // DefaultKnowledgeMaxBytesPerSource caps how many bytes are read per source. DefaultKnowledgeMaxBytesPerSource = 32 * 1024 // DefaultKnowledgeMaxSourcesPerService caps how many sources one service may inject. DefaultKnowledgeMaxSourcesPerService = 8 // TruncationMarker is appended to a source body that hit the byte cap. TruncationMarker = "[knowledge truncated at size cap]" )
Knowledge composition defaults (Tier 1).
const ( // RememberMemoryToolName is the model-facing name of the fact-writing tool. RememberMemoryToolName = "memory_remember" // ForgetMemoryToolName is the model-facing name of the fact-deleting tool. ForgetMemoryToolName = "memory_forget" )
Project memory: durable, project-scoped facts (goals, conventions, decisions, standing constraints) that persist across conversations and are visible to every user working on the same project — distinct from conversation history, which is per-(project, contextId) and windowed. See internal/memory for the storage layer.
const ( // LoadSkillToolName is the model-facing name of the built-in skill loader. LoadSkillToolName = "load_skill" // DefaultSkillTimeout bounds one skill-body fetch. DefaultSkillTimeout = 5 * time.Second // DefaultSkillMaxBytes caps a skill body (a skill is a procedure, not a // document dump). DefaultSkillMaxBytes = 64 * 1024 )
Skills composition: providers publish reviewed procedures (see Skill); composition puts only their names + descriptions into the system prompt ("progressive disclosure") and exposes one built-in tool, load_skill, that fetches a skill's body on demand. The body is provider-authored INSTRUCTION content — the platform prompt scopes what a loaded skill may direct (only that provider's services, never overriding platform rules), and skills grant no privileges beyond the independently allow-listed tools.
const GapReportToolBaseName = "report_capability_gap"
GapReportToolBaseName is the un-namespaced model-facing tool name for capability-gap reporting. Each provider service that declares a ReportingProject gets its own namespaced instance — see GapReportToolName — so the model can only report a gap against a service actually present in the current conversation, never an arbitrary provider it names in free text.
const MaxInjectedCapabilityKeys = 40
MaxInjectedCapabilityKeys caps how many of a service's existing capability keys are shown to the model in the gap-report tool schema, which sits in EVERY turn's prompt for an entitled service. Truncating is safe because the list is most-hit first — a cap can only drop the keys least likely to be filed again.
Variables ¶
This section is empty.
Functions ¶
func GapReportToolName ¶
GapReportToolName renders the model-facing name for one provider's gap- report tool, "report_capability_gap__<serviceRef>", sanitized the same way provider MCP tools are namespaced (see NamespaceToolName).
func NamespaceToolName ¶
NamespaceToolName renders the model-facing name "<server>__<tool>", with both parts sanitized to the [a-zA-Z0-9_-] set model providers require.
func SanitizeName ¶
SanitizeName reduces a provider-supplied name to the character set tool and skill identifiers use. Exported so anything deriving an identifier from a document (the agent card's per-service skill IDs) applies the same rule as NamespaceToolName rather than passing an arbitrary provider string through.
Types ¶
type Authority ¶
type Authority struct {
Reads []AuthorityRead `json:"reads,omitempty"`
MaxTaskDurationSeconds *int `json:"maxTaskDurationSeconds,omitempty"`
}
Authority describes the read scope and time budget granted to the agent.
type AuthorityRead ¶
type AuthorityRead struct {
GVK GVKRef `json:"gvk"`
}
AuthorityRead names a resource kind the agent is authorized to read.
type CallerIdentity ¶
type CallerIdentity struct {
// BearerToken is the raw token from the authenticated request. Empty means
// there is no identity to forward (any non-HTTP entry point).
BearerToken string
}
CallerIdentity is the calling user's own credential, threaded through composition so a sanctioned provider endpoint can act as that user. String and LogValue redact the token: a stray %v or slog attribute must never be able to write a live credential to a log.
func (CallerIdentity) LogValue ¶
func (c CallerIdentity) LogValue() slog.Value
func (CallerIdentity) String ¶
func (c CallerIdentity) String() string
type CapabilityDocument ¶
type CapabilityDocument struct {
APIVersion string `json:"apiVersion,omitempty"`
Kind string `json:"kind,omitempty"`
Metadata *Metadata `json:"metadata,omitempty"`
Spec CapabilitySpec `json:"spec"`
Status *Status `json:"status,omitempty"`
}
CapabilityDocument is one project-scoped entitlement: a provider service and the capabilities it grants the assistant. The JSON shape matches the CRD projection; unknown fields are ignored on parse so newer projections never break an older assistant.
func ParseDocuments ¶
func ParseDocuments(raw []byte, onSkip func(index int, err error)) ([]CapabilityDocument, error)
ParseDocuments parses a capability-document fixture: either a bare JSON array of documents or a List object ({"items": [...]}). Entries that fail CapabilityDocument.Validate are skipped and reported to onSkip (if non-nil) rather than failing the whole file, so one malformed document never takes down a project's capabilities. A malformed root (bad JSON, or neither array nor list) is a hard error.
func ScopeDocuments ¶
func ScopeDocuments(docs []CapabilityDocument, expectedProject string, logger *slog.Logger) []CapabilityDocument
ScopeDocuments is the defense-in-depth tenant-isolation seam. The capability Source is trusted to return only the calling project's documents; this guards against a Source bug (or a compromised fan-out) leaking another tenant's document by dropping any whose Metadata.Namespace names a different project. It fails closed only on a positive mismatch: a document with no namespace is kept, because the schema carries no other project handle to cross-check and the Source stays the scoping authority there. With no ExpectedProject the check is disabled and docs pass through unchanged (backward compatible).
func (*CapabilityDocument) Validate ¶
func (d *CapabilityDocument) Validate() error
Validate reports whether the document satisfies the required-field constraints (the Go analogue of the zod schema). It returns a clear, path-qualified error on the first violation. Unknown fields are not an error; missing or empty required fields are.
type CapabilitySpec ¶
type CapabilitySpec struct {
ServiceRef Ref `json:"serviceRef"`
ServiceName string `json:"serviceName"`
ServiceAgentRef Ref `json:"serviceAgentRef"`
ConfigurationVersion string `json:"configurationVersion"`
Knowledge *Knowledge `json:"knowledge,omitempty"`
Tools *Tools `json:"tools,omitempty"`
Skills []Skill `json:"skills,omitempty"`
Authority *Authority `json:"authority,omitempty"`
// ReportingProject is the Milo project where this service's own team
// reviews capability-gap reports (see internal/gapreport) — resolved by
// the service catalog from its own service registration, distinct from
// Metadata.Namespace (the consumer project this document was entitled
// to). Optional: when a document declares Tools but no
// ReportingProject, capability-gap reporting is simply unavailable for
// that service (no tool is registered) rather than an error.
ReportingProject string `json:"reportingProject,omitempty"`
}
CapabilitySpec is the meat of a capability document: which provider service it entitles and the knowledge/tools/authority it projects.
type ComposeOptions ¶
type ComposeOptions struct {
// HTTPClient fetches knowledge sources. Nil uses http.DefaultClient.
HTTPClient *http.Client
// KnowledgeTimeout, KnowledgeMaxBytesPerSource, KnowledgeMaxSourcesPerService
// override the Tier-1 defaults when > 0.
KnowledgeTimeout time.Duration
KnowledgeMaxBytesPerSource int
KnowledgeMaxSourcesPerService int
// MCPConnectTimeout overrides [DefaultMCPConnectTimeout] when > 0.
MCPConnectTimeout time.Duration
// SkillTimeout and SkillMaxBytes override the skill-body fetch defaults
// when > 0 (see DefaultSkillTimeout / DefaultSkillMaxBytes).
SkillTimeout time.Duration
SkillMaxBytes int
// OnToolInvocation, if set, fires once at the start of every provider-tool
// execution (wired to usage metering by the caller).
OnToolInvocation func(ProviderToolInvocation)
// Logger receives composition warnings. Nil discards them.
Logger *slog.Logger
// AllowPrivateNetworks disables the SSRF IP guard's private/loopback/
// link-local block for the knowledge, skill, and MCP fetches. It is the
// dev/cluster escape hatch: local overlays address services over loopback
// and in-cluster (private) IPs, which the guard blocks by default. It MUST
// stay false in production. Default false = guard on (safe).
AllowPrivateNetworks bool
// AllowedHosts and AllowedCIDRs, when either is non-empty, switch the SSRF
// guard from its default "block private" IP-policy into an allow-list posture
// for UNTRUSTED providers: a capability-document URL (knowledge source, skill
// source, MCP endpoint) is permitted only if its host matches an AllowedHosts
// entry or resolves into an AllowedCIDRs range. AllowedHosts entries match a
// host exactly and as a domain suffix ("example.com" permits "example.com"
// and "api.example.com"); AllowedCIDRs is the reviewed gateway range(s).
//
// The always-blocked set (link-local/IMDS, unspecified, multicast) still
// holds in allow-list mode — an allow-listed host that resolves to metadata
// is refused. When both are empty the guard keeps its IP-policy behavior
// (backward compatible). The integrator populates these from config.
AllowedHosts []string
AllowedCIDRs []string
// Caller is the calling user's own credential, forwarded (with
// ExpectedProject) only to endpoints in IdentityForwardHosts so a provider
// can read as the caller instead of holding standing access of its own.
Caller CallerIdentity
// IdentityForwardHosts is the OPERATOR-sanctioned set of MCP endpoint hosts
// that may receive Caller — matched exactly and as a domain suffix. Kept
// separate from the SSRF allow-list on purpose: that one answers "may we
// connect at all", this the far narrower "may we hand this endpoint the
// user's credential". Empty (the default) forwards to nobody. See identity.go.
IdentityForwardHosts []string
// ExpectedProject, when set, is the namespace/project of the calling request.
// It is a defense-in-depth tenant-isolation check on the capability Source:
// the Source is responsible for returning only the calling project's
// documents, but any document whose Metadata.Namespace disagrees with
// ExpectedProject is dropped and logged rather than trusted. Documents that
// carry no namespace are passed through — for those the Source remains the
// scoping authority (the CRD projection has no spec-level project field to
// cross-check; if one is added later, extend ScopeDocuments to verify it).
ExpectedProject string
// Memory, when non-nil, enables the memory_remember / memory_forget
// built-in tools (see internal/capability/memory.go) scoped to
// ExpectedProject, plus a "Project memory:" addendum section listing the
// project's current facts. Nil disables the feature entirely — no tools,
// no addendum section. Also requires ExpectedProject to be set, since the
// tools need a project to scope reads/writes to.
Memory memory.Store
// ContextID, when set, is the conversation this composition is running
// for. It is threaded into gap reports as provenance only (see
// GapReports) — it plays no role in tool scoping or tenant isolation.
ContextID string
// GapReports, when non-nil, enables one report_capability_gap__<service>
// tool per composed document that declares spec.reportingProject (see
// internal/capability/gapreport.go): the model can flag that a provider
// service is missing a tool or lookup a user needed, and the report is
// written to that PROVIDER's own project — never ExpectedProject, the
// consumer's project — so the team that owns the missing capability sees
// it without the user having to report it themselves. A document with
// tools but no reportingProject simply gets no gap-report tool. Nil
// disables the feature entirely.
GapReports gapreport.Store
// PlatformAPI, when non-nil, adds the base platform tools (see
// internal/basetools) to every project's composition: read this project's
// resources of any kind, describe a kind, list where a service is offered,
// report what the allowance has left. They are not one provider's
// contribution, so they are not namespaced under a service and are not
// allow-listed by a capability document — they are what every project has.
//
// They run as the CALLER and only as the caller: the client is bound to
// Caller.BearerToken and ExpectedProject here, once, and the tools receive
// a view that carries no other identity. With either missing there is
// nobody to act as, so nothing is composed. Nil disables the feature.
PlatformAPI *projectapi.Client
// PlanTokenKey binds plans for the base tools' change path
// (resources_validate, resources_plan, resources_apply). Empty leaves the
// change path out: a service that cannot check a token must not issue one.
// Ignored when PlatformAPI is nil. See internal/plantoken.
PlanTokenKey []byte
// Metrics, when non-nil, records assistant_gap_report_total for every
// report_capability_gap tool call this composition creates (see
// internal/metrics). Nil disables recording only — GapReports still
// governs whether the tool is composed at all.
Metrics *appmetrics.Metrics
// contains filtered or unexported fields
}
ComposeOptions configures Compose. All fields are optional.
type Composed ¶
type Composed struct {
// SystemPromptAddendum is "" when no document contributed knowledge.
SystemPromptAddendum string
// Tools holds the allow-listed provider tools, keyed and named
// "<server>__<tool>", together with the platform's own built-ins.
Tools agentcore.ToolSet
// Mutating names the composed tools on a change path: those a capability
// document flagged in mcpServers[].mutating, plus the base tools' change
// path. It answers what this project's assistant can change. Sorted, so
// two compositions of the same project read alike.
Mutating []string
// contains filtered or unexported fields
}
Composed is the result of Compose: the knowledge addendum for the system prompt and the allow-listed, namespaced provider tools ready to drive the loop. Close tears down the per-request MCP sessions.
func Compose ¶
func Compose(ctx context.Context, docs []CapabilityDocument, opts ComposeOptions) (*Composed, error)
Compose turns a project's capability documents into composed capabilities: it fetches knowledge into a provenance-labelled addendum and connects each document's MCP servers, exposing only the allow-listed tools, namespaced and de-collided. A server that cannot be reached (or a tool that is missing) contributes nothing and is logged — it never fails the whole composition.
func (*Composed) Close ¶
Close closes every MCP session opened during composition. It is safe to call more than once.
func (*Composed) IsMutating ¶
IsMutating reports whether a composed tool is on a change path.
type Condition ¶
type Condition struct {
Type string `json:"type"`
Status string `json:"status"`
Reason string `json:"reason,omitempty"`
Message string `json:"message,omitempty"`
}
Condition is a status condition on the document.
type FixtureSource ¶
type FixtureSource struct {
// contains filtered or unexported fields
}
FixtureSource is a Source backed by a JSON file of capability documents — the output of a project-scoped export. The file path is injected (not read from the environment) so the type stays env-free and testable.
func NewFixtureSource ¶
func NewFixtureSource(path string, logger *slog.Logger) *FixtureSource
NewFixtureSource returns a fixture source reading path. A nil logger discards skip warnings.
func (*FixtureSource) Documents ¶
func (s *FixtureSource) Documents(_ context.Context, _ string) ([]CapabilityDocument, error)
Documents reads and parses the fixture file. The export is already project-scoped, so projectName does not filter here (that is the HTTP source's job). Individual documents that fail validation are skipped with a warning; a missing file or malformed root is an error.
type GVKRef ¶
GVKRef is the {group, kind} reference style used across the service catalog (no version).
type HTTPSource ¶
type HTTPSource struct {
// contains filtered or unexported fields
}
HTTPSource is a Source backed by the capability-provider HTTP API. For each project it fetches
GET {baseURL}/projects/{projectName}/capability-documents
and parses the response with the same validation path as FixtureSource (unknown fields ignored, individual invalid documents skipped with a warning). There is no caching in v0 — every call performs a fresh fetch.
The source degrades rather than fails: any transport error, non-2xx status, body-read error, or malformed root JSON is logged and yields an empty document set (nil error). That matches the assistant's fixture-missing behavior — the agent composes built-ins only — so a provider outage can never fail a chat. It never panics.
func NewHTTPSource ¶
NewHTTPSource returns an HTTP source targeting baseURL (the capability provider's base URL, e.g. http://capability-adapter). A trailing slash is tolerated. A nil client uses a default client with a 5s timeout; a nil logger discards degradation warnings.
func (*HTTPSource) Documents ¶
func (s *HTTPSource) Documents(ctx context.Context, projectName string) ([]CapabilityDocument, error)
Documents fetches and parses projectName's capability documents. On any failure it logs and returns an empty set (nil error) — see HTTPSource.
type Knowledge ¶
type Knowledge struct {
Sources []KnowledgeSource `json:"sources,omitempty"`
Concepts []KnowledgeConcept `json:"concepts,omitempty"`
}
Knowledge is the Tier-1 knowledge a provider contributes.
type KnowledgeConcept ¶
KnowledgeConcept is a short, provider-authored gloss on one of its resource kinds.
type KnowledgeSource ¶
type KnowledgeSource struct {
Type KnowledgeSourceType `json:"type"`
Title string `json:"title,omitempty"`
URL string `json:"url"`
}
KnowledgeSource is a fetchable provider document.
type KnowledgeSourceType ¶
type KnowledgeSourceType string
KnowledgeSourceType is the kind of a knowledge source document.
const ( KnowledgeLLMDocs KnowledgeSourceType = "LLMDocs" KnowledgeRunbook KnowledgeSourceType = "Runbook" KnowledgeMarkdown KnowledgeSourceType = "Markdown" )
type MCPServer ¶
type MCPServer struct {
Name string `json:"name"`
Endpoint string `json:"endpoint"`
ToolSelector ToolSelector `json:"toolSelector"`
Mutating []string `json:"mutating,omitempty"`
}
MCPServer is one provider MCP server the assistant may connect to.
type Metadata ¶
type Metadata struct {
Name string `json:"name,omitempty"`
Namespace string `json:"namespace,omitempty"`
}
Metadata mirrors the object metadata carried on the CRD projection.
type ProviderToolInvocation ¶
type ProviderToolInvocation struct {
// ServiceName is the reverse-DNS provider service name from the document.
ServiceName string
// ServerName is the mcpServers[].name shortname the tool is namespaced under.
ServerName string
// ToolName is the original (un-namespaced) provider tool name.
ToolName string
// NamespacedToolName is the name the model sees, "<server>__<tool>".
NamespacedToolName string
}
ProviderToolInvocation is reported once per provider-tool execution (the metering hook). It identifies the provider service and both the raw and namespaced tool names.
type ServiceEntitlement ¶
type ServiceEntitlement struct {
// ID is the sanitized serviceRef: the stable identifier a card advertises,
// run through [SanitizeName] so a provider-supplied name cannot put an
// arbitrary string in a field consumers treat as an ID.
ID string
ServiceRef string // spec.serviceRef.name, verbatim — "streamco"
ServiceName string // spec.serviceName — "streaming.streamco.example"
ToolNames []string // namespaced "<server>__<tool>", from ToolSelector.Include ONLY
MCPEndpoints []string // spec.tools.mcpServers[].endpoint
SkillNames []string // namespaced "<serviceRef>__<skill>"
SkillDescriptions []string
KnowledgeTitles []string // knowledge source titles (or URLs when untitled)
}
ServiceEntitlement is one entitled provider service, derived STRICTLY from that project's capability documents — nothing global, nothing inferred, and nothing learned from a live MCP connection.
func Entitlements ¶
func Entitlements(docs []CapabilityDocument) []ServiceEntitlement
Entitlements derives one entry per entitled service from ALREADY-SCOPED documents. Callers MUST pass the output of ScopeDocuments: a Source may return more than the calling project's documents (FixtureSource ignores the project entirely), and that gate is the only thing standing between a project's card and another project's services.
It reads documents, never a Composed, so Patch's own built-ins (load_skill, memory_remember, memory_forget, report_capability_gap__*) can never leak into what is advertised as provider surface. It performs no MCP connect either: the declared ToolSelector.Include list is what a project is entitled to, which is a claim about entitlement, not about health.
type Skill ¶
type Skill struct {
Name string `json:"name"`
Description string `json:"description"`
// Source is the HTTP(S) URL of the skill body (markdown/plain text).
Source string `json:"source"`
}
Skill is a provider-published, reviewed procedure the assistant may follow — the middle rung between knowledge (facts) and tools (callable endpoints). Only Name and Description enter the prompt; the body at Source is fetched on demand via the built-in load_skill tool (progressive disclosure), so a provider can publish many skills at near-zero prompt cost. A skill never grants privileges: it can only direct the model toward tools that are independently allow-listed.
type Source ¶
type Source interface {
// Documents returns the capability documents entitling projectName.
Documents(ctx context.Context, projectName string) ([]CapabilityDocument, error)
}
Source supplies the capability documents that apply to a project. The local slice uses FixtureSource (a JSON file exported from the control plane); a production HTTP-backed source is a follow-up behind this same seam.
type Status ¶
type Status struct {
Conditions []Condition `json:"conditions,omitempty"`
}
Status carries the document's status conditions.
type ToolSelector ¶
type ToolSelector struct {
Include []string `json:"include,omitempty"`
}
ToolSelector is the client-side allow-list of tool names to expose from an MCP server.