mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-07-10 00:14:38 +00:00
Manifest-backed MCP tools, prompts, and resources with surface affordance contracts; agent capability manifest and governance projection; API contract tests and capability route projection; operations-loop and intelligence-funnel telemetry; release-control subsystem documentation, registry, and tooling; licensing and configuration.
518 lines
21 KiB
Go
518 lines
21 KiB
Go
package agentcapabilities
|
|
|
|
import (
|
|
"reflect"
|
|
"testing"
|
|
)
|
|
|
|
func TestDefaultSurfaceAffordancesPinSupportedSurfaces(t *testing.T) {
|
|
assistant := DefaultSurfaceAffordancesForID(SurfaceIDPulseAssistant)
|
|
if !assistant.Tools || !assistant.InteractiveQuestions {
|
|
t.Fatalf("Pulse Assistant affordances = %+v, want tools and interactive questions", assistant)
|
|
}
|
|
if assistant.Resources || assistant.Prompts || assistant.CapabilityMetadata {
|
|
t.Fatalf("Pulse Assistant must not default to MCP-only affordances: %+v", assistant)
|
|
}
|
|
|
|
mcp := DefaultSurfaceAffordancesForID(SurfaceIDPulseMCP)
|
|
if !mcp.Tools || !mcp.Resources || !mcp.Prompts || !mcp.CapabilityMetadata {
|
|
t.Fatalf("Pulse MCP affordances = %+v, want tools/resources/prompts/capability metadata", mcp)
|
|
}
|
|
if mcp.InteractiveQuestions {
|
|
t.Fatalf("Pulse MCP must not default to native interactive questions: %+v", mcp)
|
|
}
|
|
}
|
|
|
|
func TestNormalizeSurfaceAffordancesPreservesExplicitManifestValues(t *testing.T) {
|
|
surface := OperatorSurfaceContract{
|
|
ID: SurfaceIDPulseMCP,
|
|
Affordances: SurfaceAffordanceContract{
|
|
Tools: true,
|
|
},
|
|
}
|
|
|
|
got := NormalizeSurfaceAffordances(surface)
|
|
if !got.Tools {
|
|
t.Fatalf("explicit tools affordance lost: %+v", got)
|
|
}
|
|
if got.Resources || got.Prompts || got.CapabilityMetadata || got.InteractiveQuestions {
|
|
t.Fatalf("explicit affordances must not be expanded by defaults: %+v", got)
|
|
}
|
|
}
|
|
|
|
func TestResolveSurfaceAffordanceContractResolvesLabelsAndDefaults(t *testing.T) {
|
|
canonical, ok := ResolveSurfaceAffordanceContract(CanonicalManifest().SurfaceContract, SurfaceIDPulseMCP, "")
|
|
if !ok {
|
|
t.Fatal("canonical Pulse MCP surface must resolve from manifest")
|
|
}
|
|
if canonical.SurfaceID != SurfaceIDPulseMCP || canonical.SurfaceLabel != "Pulse MCP" {
|
|
t.Fatalf("canonical Pulse MCP surface identity = %+v", canonical)
|
|
}
|
|
if !canonical.Affordances.Tools || !canonical.Affordances.Resources || !canonical.Affordances.Prompts || !canonical.Affordances.CapabilityMetadata {
|
|
t.Fatalf("canonical Pulse MCP affordances = %+v", canonical.Affordances)
|
|
}
|
|
|
|
legacyAssistant, ok := ResolveSurfaceAffordanceContract(SurfaceContract{}, SurfaceIDPulseAssistant, "")
|
|
if ok {
|
|
t.Fatal("legacy Assistant fallback must not report a declared surface")
|
|
}
|
|
if legacyAssistant.SurfaceLabel != "Pulse Assistant" || !legacyAssistant.Affordances.Tools || !legacyAssistant.Affordances.InteractiveQuestions {
|
|
t.Fatalf("legacy Assistant fallback = %+v", legacyAssistant)
|
|
}
|
|
if legacyAssistant.Affordances.Resources || legacyAssistant.Affordances.Prompts || legacyAssistant.Affordances.CapabilityMetadata {
|
|
t.Fatalf("legacy Assistant fallback must not inherit MCP affordances: %+v", legacyAssistant.Affordances)
|
|
}
|
|
|
|
custom, ok := ResolveSurfaceAffordanceContract(SurfaceContract{}, "custom_external_agent", "")
|
|
if ok {
|
|
t.Fatal("unknown custom surface fallback must not report a declared surface")
|
|
}
|
|
if custom.SurfaceLabel != "custom_external_agent" || SurfaceAffordancesDeclared(custom.Affordances) {
|
|
t.Fatalf("unknown custom surface fallback = %+v, want id label and empty affordances", custom)
|
|
}
|
|
}
|
|
|
|
func TestManifestSurfaceAffordancesResolvesDeclaredAndDefaultSurfaces(t *testing.T) {
|
|
defaults, ok := ManifestSurfaceAffordances(Manifest{}, SurfaceIDPulseMCP)
|
|
if ok {
|
|
t.Fatal("legacy manifest fallback must report compatibility default, not a declared surface")
|
|
}
|
|
if !defaults.Tools || !defaults.Resources || !defaults.Prompts || !defaults.CapabilityMetadata || defaults.InteractiveQuestions {
|
|
t.Fatalf("Pulse MCP default affordances = %+v", defaults)
|
|
}
|
|
|
|
manifest := Manifest{SurfaceContract: SurfaceContract{OperatorSurfaces: []OperatorSurfaceContract{{
|
|
ID: "custom_external_agent",
|
|
Label: "Custom external agent",
|
|
ExternalAdapter: true,
|
|
Affordances: SurfaceAffordanceContract{
|
|
Tools: true,
|
|
Resources: true,
|
|
},
|
|
}}}}
|
|
declared, ok := ManifestSurfaceAffordances(manifest, "custom_external_agent")
|
|
if !ok {
|
|
t.Fatal("declared custom surface was not found")
|
|
}
|
|
if !declared.Tools || !declared.Resources || declared.Prompts || declared.CapabilityMetadata || declared.InteractiveQuestions {
|
|
t.Fatalf("declared custom surface affordances = %+v", declared)
|
|
}
|
|
|
|
if unknown, ok := ManifestSurfaceAffordances(Manifest{}, "unknown_surface"); ok || SurfaceAffordancesDeclared(unknown) {
|
|
t.Fatalf("unknown surface affordances = %+v ok=%v, want empty", unknown, ok)
|
|
}
|
|
}
|
|
|
|
func TestSurfaceAffordanceLabelsAreStable(t *testing.T) {
|
|
got := SurfaceAffordanceLabels(SurfaceAffordanceContract{
|
|
Tools: true,
|
|
Resources: true,
|
|
Prompts: true,
|
|
CapabilityMetadata: true,
|
|
InteractiveQuestions: true,
|
|
})
|
|
want := []string{"tools", "resources", "prompts", "capability metadata", "interactive questions"}
|
|
if len(got) != len(want) {
|
|
t.Fatalf("labels = %#v, want %#v", got, want)
|
|
}
|
|
for i := range want {
|
|
if got[i] != want[i] {
|
|
t.Fatalf("labels = %#v, want %#v", got, want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestFindOperatorSurfaceContractMatchesIDOrLabel(t *testing.T) {
|
|
contract := CanonicalManifest().SurfaceContract
|
|
if surface, ok := FindOperatorSurfaceContract(contract, SurfaceIDPulseAssistant, ""); !ok || surface.Label != "Pulse Assistant" {
|
|
t.Fatalf("find by id = %+v ok=%v", surface, ok)
|
|
}
|
|
if surface, ok := FindOperatorSurfaceContract(contract, "", "Pulse MCP"); !ok || surface.ID != SurfaceIDPulseMCP {
|
|
t.Fatalf("find by label = %+v ok=%v", surface, ok)
|
|
}
|
|
}
|
|
|
|
func TestSurfaceToolContractNormalizeCollectionsDetachesNames(t *testing.T) {
|
|
source := []string{PulseQueryToolName}
|
|
contract := SurfaceToolContract{ToolNames: source}.NormalizeCollections()
|
|
if len(contract.ToolNames) != 1 || contract.ToolNames[0] != PulseQueryToolName {
|
|
t.Fatalf("normalized names = %#v", contract.ToolNames)
|
|
}
|
|
contract.ToolNames[0] = PulseReadToolName
|
|
if source[0] != PulseQueryToolName {
|
|
t.Fatalf("NormalizeCollections returned aliased tool names: source=%#v normalized=%#v", source, contract.ToolNames)
|
|
}
|
|
|
|
empty := SurfaceToolContract{}.NormalizeCollections()
|
|
if empty.ToolNames == nil || len(empty.ToolNames) != 0 {
|
|
t.Fatalf("empty tool names = %#v, want stable empty list", empty.ToolNames)
|
|
}
|
|
}
|
|
|
|
func TestCloneSurfaceToolContractsDetachesNames(t *testing.T) {
|
|
if empty := CloneSurfaceToolContracts(nil); empty == nil || len(empty) != 0 {
|
|
t.Fatalf("empty surface tool contracts = %#v, want stable empty list", empty)
|
|
}
|
|
|
|
source := []SurfaceToolContract{{
|
|
SurfaceID: SurfaceIDPulseMCP,
|
|
ToolNames: []string{FleetContextCapabilityName},
|
|
CapabilityNames: []string{FleetContextCapabilityName},
|
|
RegistryToolNames: []string{PulseReadToolName},
|
|
NativeToolNames: []string{PulseQuestionToolName},
|
|
}}
|
|
|
|
cloned := CloneSurfaceToolContracts(source)
|
|
source[0].ToolNames[0] = "mutated"
|
|
source[0].CapabilityNames[0] = "mutated"
|
|
source[0].RegistryToolNames[0] = "mutated"
|
|
source[0].NativeToolNames[0] = "mutated"
|
|
|
|
if !reflect.DeepEqual(cloned[0].ToolNames, []string{FleetContextCapabilityName}) {
|
|
t.Fatalf("cloned tool names = %#v", cloned[0].ToolNames)
|
|
}
|
|
if !reflect.DeepEqual(cloned[0].CapabilityNames, []string{FleetContextCapabilityName}) {
|
|
t.Fatalf("cloned capability names = %#v", cloned[0].CapabilityNames)
|
|
}
|
|
if !reflect.DeepEqual(cloned[0].RegistryToolNames, []string{PulseReadToolName}) {
|
|
t.Fatalf("cloned registry names = %#v", cloned[0].RegistryToolNames)
|
|
}
|
|
if !reflect.DeepEqual(cloned[0].NativeToolNames, []string{PulseQuestionToolName}) {
|
|
t.Fatalf("cloned native names = %#v", cloned[0].NativeToolNames)
|
|
}
|
|
}
|
|
|
|
func TestProjectPulseAssistantSurfaceToolContractSeparatesRegistryAndNativeTools(t *testing.T) {
|
|
contract, ok := ProjectPulseAssistantSurfaceToolContract(CanonicalManifest().SurfaceContract, []ProviderTool{
|
|
{Name: PulseQueryToolName},
|
|
{Name: PulseControlToolName},
|
|
NewPulseQuestionProviderTool(),
|
|
})
|
|
if !ok {
|
|
t.Fatal("canonical manifest must declare Pulse Assistant surface")
|
|
}
|
|
if contract.SurfaceID != SurfaceIDPulseAssistant || contract.SurfaceLabel != "Pulse Assistant" {
|
|
t.Fatalf("Assistant surface identity = %+v", contract)
|
|
}
|
|
if contract.ToolSource != SurfaceToolSourceAssistantRegistry {
|
|
t.Fatalf("Assistant tool source = %q, want %s", contract.ToolSource, SurfaceToolSourceAssistantRegistry)
|
|
}
|
|
if !reflect.DeepEqual(contract.ToolNames, []string{PulseQueryToolName, PulseControlToolName, PulseQuestionToolName}) {
|
|
t.Fatalf("Assistant tool names = %#v", contract.ToolNames)
|
|
}
|
|
if !reflect.DeepEqual(contract.RegistryToolNames, []string{PulseQueryToolName, PulseControlToolName}) {
|
|
t.Fatalf("Assistant registry names = %#v", contract.RegistryToolNames)
|
|
}
|
|
if !reflect.DeepEqual(contract.NativeToolNames, []string{PulseQuestionToolName}) {
|
|
t.Fatalf("Assistant native names = %#v", contract.NativeToolNames)
|
|
}
|
|
if len(contract.CapabilityNames) != 0 {
|
|
t.Fatalf("Assistant contract must not pretend registry tools are manifest capabilities: %#v", contract.CapabilityNames)
|
|
}
|
|
if !contract.Affordances.Tools || !contract.Affordances.InteractiveQuestions || contract.Affordances.Resources {
|
|
t.Fatalf("Assistant affordances = %+v", contract.Affordances)
|
|
}
|
|
|
|
legacy, ok := ProjectPulseAssistantSurfaceToolContract(SurfaceContract{}, []ProviderTool{
|
|
NewPulseQuestionProviderTool(),
|
|
})
|
|
if ok {
|
|
t.Fatal("legacy Assistant surface fallback must not report a declared surface")
|
|
}
|
|
if legacy.SurfaceID != SurfaceIDPulseAssistant || legacy.SurfaceLabel != "Pulse Assistant" {
|
|
t.Fatalf("legacy Assistant surface identity = %+v", legacy)
|
|
}
|
|
if !legacy.Affordances.Tools || !legacy.Affordances.InteractiveQuestions || legacy.Affordances.Resources {
|
|
t.Fatalf("legacy Assistant affordances = %+v", legacy.Affordances)
|
|
}
|
|
}
|
|
|
|
func TestProjectPulseAssistantSurfaceToolContractHonorsSurfaceAffordances(t *testing.T) {
|
|
providerTools := []ProviderTool{
|
|
{Name: PulseQueryToolName},
|
|
NewPulseQuestionProviderTool(),
|
|
}
|
|
|
|
toolsDisabled := CanonicalManifest().SurfaceContract
|
|
for i := range toolsDisabled.OperatorSurfaces {
|
|
if toolsDisabled.OperatorSurfaces[i].ID == SurfaceIDPulseAssistant {
|
|
toolsDisabled.OperatorSurfaces[i].Affordances = SurfaceAffordanceContract{InteractiveQuestions: true}
|
|
}
|
|
}
|
|
withoutTools, _ := ProjectPulseAssistantSurfaceToolContract(toolsDisabled, providerTools)
|
|
if len(withoutTools.ToolNames) != 0 || len(withoutTools.RegistryToolNames) != 0 || len(withoutTools.NativeToolNames) != 0 {
|
|
t.Fatalf("tools-disabled Assistant surface contract = %+v, want no published tools", withoutTools)
|
|
}
|
|
|
|
questionsDisabled := CanonicalManifest().SurfaceContract
|
|
for i := range questionsDisabled.OperatorSurfaces {
|
|
if questionsDisabled.OperatorSurfaces[i].ID == SurfaceIDPulseAssistant {
|
|
questionsDisabled.OperatorSurfaces[i].Affordances = SurfaceAffordanceContract{Tools: true}
|
|
}
|
|
}
|
|
withoutQuestions, _ := ProjectPulseAssistantSurfaceToolContract(questionsDisabled, providerTools)
|
|
if !reflect.DeepEqual(withoutQuestions.ToolNames, []string{PulseQueryToolName}) {
|
|
t.Fatalf("questions-disabled Assistant tool names = %#v, want registry tool only", withoutQuestions.ToolNames)
|
|
}
|
|
if len(withoutQuestions.NativeToolNames) != 0 {
|
|
t.Fatalf("questions-disabled Assistant native names = %#v, want none", withoutQuestions.NativeToolNames)
|
|
}
|
|
if !reflect.DeepEqual(withoutQuestions.RegistryToolNames, []string{PulseQueryToolName}) {
|
|
t.Fatalf("questions-disabled Assistant registry names = %#v", withoutQuestions.RegistryToolNames)
|
|
}
|
|
}
|
|
|
|
func TestIntersectSurfaceAffordancesCannotEnableDisabledSurfaceAffordance(t *testing.T) {
|
|
allowed := SurfaceAffordanceContract{
|
|
Resources: true,
|
|
Prompts: true,
|
|
CapabilityMetadata: true,
|
|
}
|
|
requested := SurfaceAffordanceContract{
|
|
Tools: true,
|
|
Resources: true,
|
|
Prompts: true,
|
|
CapabilityMetadata: true,
|
|
}
|
|
|
|
got := IntersectSurfaceAffordances(allowed, requested)
|
|
if got.Tools {
|
|
t.Fatalf("intersection re-enabled disabled tools affordance: %+v", got)
|
|
}
|
|
if !got.Resources || !got.Prompts || !got.CapabilityMetadata {
|
|
t.Fatalf("intersection dropped allowed requested affordances: %+v", got)
|
|
}
|
|
if got.InteractiveQuestions {
|
|
t.Fatalf("intersection enabled disabled interactive questions affordance: %+v", got)
|
|
}
|
|
}
|
|
|
|
func TestProjectManifestSurfaceToolContractUsesPublishedSurfaceToolContract(t *testing.T) {
|
|
manifest := Manifest{
|
|
SurfaceContract: CanonicalManifest().SurfaceContract,
|
|
SurfaceToolContracts: []SurfaceToolContract{{
|
|
SurfaceID: SurfaceIDPulseMCP,
|
|
ToolSource: SurfaceToolSourceCapabilityManifest,
|
|
ToolNames: []string{
|
|
SetOperatorStateCapabilityName,
|
|
ResourceContextCapabilityName,
|
|
EventSubscriptionCapabilityName,
|
|
"missing_tool",
|
|
SetOperatorStateCapabilityName,
|
|
},
|
|
}},
|
|
Capabilities: []Capability{
|
|
{Name: FleetContextCapabilityName, Description: "Read fleet context."},
|
|
{Name: EventSubscriptionCapabilityName, Description: "Stream events."},
|
|
{Name: ResourceContextCapabilityName, Description: "Read resource context."},
|
|
{Name: SetOperatorStateCapabilityName, Description: "Set operator state."},
|
|
},
|
|
}
|
|
|
|
contract, ok := ProjectManifestSurfaceToolContract(manifest, SurfaceIDPulseMCP)
|
|
if !ok {
|
|
t.Fatal("canonical manifest must declare Pulse MCP surface")
|
|
}
|
|
if contract.SurfaceID != SurfaceIDPulseMCP || contract.SurfaceLabel != "Pulse MCP" {
|
|
t.Fatalf("MCP surface identity = %+v", contract)
|
|
}
|
|
if contract.ToolSource != SurfaceToolSourceCapabilityManifest {
|
|
t.Fatalf("MCP tool source = %q, want %s", contract.ToolSource, SurfaceToolSourceCapabilityManifest)
|
|
}
|
|
want := []string{SetOperatorStateCapabilityName, ResourceContextCapabilityName}
|
|
if !reflect.DeepEqual(contract.ToolNames, want) {
|
|
t.Fatalf("MCP tool names = %#v, want %#v", contract.ToolNames, want)
|
|
}
|
|
if !reflect.DeepEqual(contract.CapabilityNames, want) {
|
|
t.Fatalf("MCP capability names = %#v, want %#v", contract.CapabilityNames, want)
|
|
}
|
|
if len(contract.RegistryToolNames) != 0 || len(contract.NativeToolNames) != 0 {
|
|
t.Fatalf("MCP contract must not expose Assistant registry/native tools: %+v", contract)
|
|
}
|
|
if !contract.Affordances.Tools || !contract.Affordances.Resources || !contract.Affordances.Prompts || !contract.Affordances.CapabilityMetadata || contract.Affordances.InteractiveQuestions {
|
|
t.Fatalf("MCP affordances = %+v", contract.Affordances)
|
|
}
|
|
|
|
}
|
|
|
|
func TestProjectManifestSurfaceToolContractCannotReenableDisabledSurfaceTools(t *testing.T) {
|
|
surfaceContract := CloneSurfaceContract(CanonicalManifest().SurfaceContract)
|
|
for i := range surfaceContract.OperatorSurfaces {
|
|
if surfaceContract.OperatorSurfaces[i].ID == SurfaceIDPulseMCP {
|
|
surfaceContract.OperatorSurfaces[i].Affordances = SurfaceAffordanceContract{
|
|
Resources: true,
|
|
Prompts: true,
|
|
CapabilityMetadata: true,
|
|
}
|
|
}
|
|
}
|
|
manifest := Manifest{
|
|
SurfaceContract: surfaceContract,
|
|
SurfaceToolContracts: []SurfaceToolContract{{
|
|
SurfaceID: SurfaceIDPulseMCP,
|
|
ToolSource: SurfaceToolSourceCapabilityManifest,
|
|
ToolNames: []string{FleetContextCapabilityName},
|
|
Affordances: DefaultSurfaceAffordancesForID(SurfaceIDPulseMCP),
|
|
}},
|
|
Capabilities: []Capability{
|
|
{Name: FleetContextCapabilityName, Description: "Read fleet context."},
|
|
},
|
|
}
|
|
|
|
contract, ok := ProjectManifestSurfaceToolContract(manifest, SurfaceIDPulseMCP)
|
|
if !ok {
|
|
t.Fatal("published Pulse MCP surface tool contract should still resolve")
|
|
}
|
|
if contract.Affordances.Tools {
|
|
t.Fatalf("disabled surface tools affordance was re-enabled: %+v", contract.Affordances)
|
|
}
|
|
if len(contract.ToolNames) != 0 || len(contract.CapabilityNames) != 0 {
|
|
t.Fatalf("tools-disabled external surface exposed tool names: %+v", contract)
|
|
}
|
|
if !contract.Affordances.Resources || !contract.Affordances.Prompts || !contract.Affordances.CapabilityMetadata {
|
|
t.Fatalf("allowed non-tool affordances were lost: %+v", contract.Affordances)
|
|
}
|
|
}
|
|
|
|
func TestProjectManifestSurfaceToolContractRequiresPublishedSurfaceToolContract(t *testing.T) {
|
|
manifest := Manifest{
|
|
SurfaceContract: CanonicalManifest().SurfaceContract,
|
|
Capabilities: []Capability{
|
|
{Name: FleetContextCapabilityName, Description: "Read fleet context."},
|
|
{Name: EventSubscriptionCapabilityName, Description: "Stream events."},
|
|
{Name: ResourceContextCapabilityName, Description: "Read resource context."},
|
|
},
|
|
}
|
|
|
|
if contract, ok := ProjectManifestSurfaceToolContract(manifest, SurfaceIDPulseMCP); ok {
|
|
t.Fatalf("missing Pulse MCP surface tool contract must fail closed, got %+v", contract)
|
|
}
|
|
if contracts := ProjectManifestSurfaceToolContracts(manifest); len(contracts) != 0 {
|
|
t.Fatalf("missing external surface tool contract projected tools: %+v", contracts)
|
|
}
|
|
}
|
|
|
|
func TestProjectManifestSurfaceToolContractRejectsNativeOrMissingSurfaces(t *testing.T) {
|
|
manifest := Manifest{
|
|
SurfaceContract: CanonicalManifest().SurfaceContract,
|
|
Capabilities: []Capability{
|
|
{Name: FleetContextCapabilityName, Description: "Read fleet context."},
|
|
},
|
|
}
|
|
|
|
if contract, ok := ProjectManifestSurfaceToolContract(manifest, SurfaceIDPulseAssistant); ok {
|
|
t.Fatalf("native Assistant surface must not project static manifest tools: %+v", contract)
|
|
}
|
|
if contract, ok := ProjectManifestSurfaceToolContract(manifest, "missing_surface"); ok {
|
|
t.Fatalf("missing surface must not project static manifest tools: %+v", contract)
|
|
}
|
|
}
|
|
|
|
func TestProjectManifestSurfaceToolContractsReturnsExternalAdaptersInManifestOrder(t *testing.T) {
|
|
surfaceContract := CanonicalManifest().SurfaceContract
|
|
surfaceContract.OperatorSurfaces = append(surfaceContract.OperatorSurfaces, OperatorSurfaceContract{
|
|
ID: "pulse_cli_agent",
|
|
Label: "Pulse CLI Agent",
|
|
ExternalAdapter: true,
|
|
Affordances: SurfaceAffordanceContract{
|
|
Tools: true,
|
|
CapabilityMetadata: true,
|
|
},
|
|
})
|
|
manifest := Manifest{
|
|
SurfaceContract: surfaceContract,
|
|
SurfaceToolContracts: []SurfaceToolContract{
|
|
{
|
|
SurfaceID: SurfaceIDPulseMCP,
|
|
ToolSource: SurfaceToolSourceCapabilityManifest,
|
|
ToolNames: []string{FleetContextCapabilityName},
|
|
},
|
|
{
|
|
SurfaceID: "pulse_cli_agent",
|
|
ToolSource: SurfaceToolSourceCapabilityManifest,
|
|
ToolNames: []string{PlanActionCapabilityName},
|
|
},
|
|
},
|
|
Capabilities: []Capability{
|
|
{Name: FleetContextCapabilityName, Description: "Read fleet context."},
|
|
{Name: EventSubscriptionCapabilityName, Description: "Stream events."},
|
|
{Name: PlanActionCapabilityName, Description: "Plan a governed action."},
|
|
},
|
|
}
|
|
|
|
contracts := ProjectManifestSurfaceToolContracts(manifest)
|
|
if len(contracts) != 2 {
|
|
t.Fatalf("manifest surface contracts = %+v, want MCP and custom external adapter", contracts)
|
|
}
|
|
if contracts[0].SurfaceID != SurfaceIDPulseMCP || contracts[1].SurfaceID != "pulse_cli_agent" {
|
|
t.Fatalf("manifest external surface order = %+v", contracts)
|
|
}
|
|
if !reflect.DeepEqual(contracts[0].ToolNames, []string{FleetContextCapabilityName}) {
|
|
t.Fatalf("MCP surface tool names = %#v", contracts[0].ToolNames)
|
|
}
|
|
if !reflect.DeepEqual(contracts[1].ToolNames, []string{PlanActionCapabilityName}) {
|
|
t.Fatalf("custom external surface tool names = %#v", contracts[1].ToolNames)
|
|
}
|
|
for _, contract := range contracts {
|
|
if contract.ToolSource != SurfaceToolSourceCapabilityManifest {
|
|
t.Fatalf("external surface source = %q", contract.ToolSource)
|
|
}
|
|
for _, name := range contract.ToolNames {
|
|
if name == EventSubscriptionCapabilityName {
|
|
t.Fatalf("external surface tool names included streaming capability: %#v", contract.ToolNames)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestProjectPulseIntelligenceSurfaceToolContractsPreservesManifestSurfaceOrder(t *testing.T) {
|
|
surfaceContract := CanonicalManifest().SurfaceContract
|
|
surfaceContract.OperatorSurfaces = append(surfaceContract.OperatorSurfaces, OperatorSurfaceContract{
|
|
ID: "pulse_cli_agent",
|
|
Label: "Pulse CLI Agent",
|
|
ExternalAdapter: true,
|
|
Affordances: SurfaceAffordanceContract{
|
|
Tools: true,
|
|
},
|
|
})
|
|
manifest := Manifest{
|
|
SurfaceContract: surfaceContract,
|
|
SurfaceToolContracts: []SurfaceToolContract{
|
|
{
|
|
SurfaceID: SurfaceIDPulseMCP,
|
|
ToolSource: SurfaceToolSourceCapabilityManifest,
|
|
ToolNames: []string{FleetContextCapabilityName},
|
|
},
|
|
{
|
|
SurfaceID: "pulse_cli_agent",
|
|
ToolSource: SurfaceToolSourceCapabilityManifest,
|
|
ToolNames: []string{},
|
|
},
|
|
},
|
|
Capabilities: []Capability{
|
|
{Name: FleetContextCapabilityName, Description: "Read fleet context."},
|
|
{Name: PlanActionCapabilityName, Description: "Plan a governed action."},
|
|
{Name: EventSubscriptionCapabilityName, Description: "Stream events."},
|
|
},
|
|
}
|
|
contracts := ProjectPulseIntelligenceSurfaceToolContracts(manifest, []ProviderTool{
|
|
{Name: PulseQueryToolName},
|
|
NewPulseQuestionProviderTool(),
|
|
})
|
|
|
|
if len(contracts) != 3 {
|
|
t.Fatalf("surface contracts = %+v, want Assistant, MCP, and custom external adapter", contracts)
|
|
}
|
|
if contracts[0].SurfaceID != SurfaceIDPulseAssistant || contracts[1].SurfaceID != SurfaceIDPulseMCP || contracts[2].SurfaceID != "pulse_cli_agent" {
|
|
t.Fatalf("surface contract order = %+v", contracts)
|
|
}
|
|
if !reflect.DeepEqual(contracts[0].ToolNames, []string{PulseQueryToolName, PulseQuestionToolName}) {
|
|
t.Fatalf("Assistant projected names = %#v", contracts[0].ToolNames)
|
|
}
|
|
if !reflect.DeepEqual(contracts[1].ToolNames, []string{FleetContextCapabilityName}) {
|
|
t.Fatalf("MCP projected names = %#v", contracts[1].ToolNames)
|
|
}
|
|
if !reflect.DeepEqual(contracts[2].ToolNames, []string{}) {
|
|
t.Fatalf("custom external adapter projected names = %#v", contracts[2].ToolNames)
|
|
}
|
|
}
|