Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ This guide owns how to work in the repository: documentation ownership, the repo
| Built-in Harness identifiers, configuration/profile bindings and display names | `internal/harnessconfig/builtin/catalog.json` and its [generated reference](contracts/agents-api/harness-catalog.md) |
| Effective MCP bindings and credential authority | [Environment MCP](contracts/agents-api/environments.md#skills-plugins-and-environment-mcp) and `apps/daemon/internal/agent/mcp_binding.go` |
| Harness qualification and acceptance | [Harness integration](contracts/agents-api/harnesses.md) |
| Harness service qualification declarations and registration | [Explicit service qualification](contracts/agents-api/harness-onboarding.md#explicit-service-qualification) and `services/core/internal/engine/profile.go` |
| Harness selection and Agent defaults | [Harness selection](contracts/agents-api/harness-selection.md) |
| Provider selection, sandbox deployment and E2B setup | [Sandbox deployment](contracts/agents-api/sandbox-deployment.md) |
| Hosted sandbox nodes | [Nodes guide](docs/getting-started/nodes.md) and [sandbox deployment contract](contracts/agents-api/sandbox-deployment.md) |
Expand Down
15 changes: 15 additions & 0 deletions contracts/agents-api/harness-onboarding.md
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,21 @@ actual Runtime capabilities remain separate checks; Runtime advertisements alone
never enable public operations. Shared dispatch checks capability combinations,
not a whitelist of engine names.

### Explicit service qualification

`engine.Profile` is the service's qualification declaration, separate from the Runtime's `AgentKindCapabilities`. Its capability fields reuse the small `proto.CapabilitySupport` value type: every field must explicitly select `CapabilitySupported` or `CapabilityUnsupported`. `CapabilityUnspecified`, including an omitted field, is rejected. Sharing this value type does not let a Runtime advertisement grant service authorization.

Each of `ConfigurationValidation`, `ToolsValidation` and `FunctionResultValidation` chooses one of two strategies:

- `CommonValidationOnly`: common schema and admission checks are sufficient. The corresponding callback must be nil; no successful placeholder callback is needed.
- `AdditionalValidation`: the matching `ValidateConfiguration`, `ValidateTools` or `ValidateFunctionResult` callback is mandatory and adds pure Harness restrictions.

An omitted or unknown policy, missing required callback, or callback paired with common-only policy is invalid. Admission follows the declared policy, never method presence. Preserve existing error precedence: configuration restrictions run first; when additional configuration validation is selected, tool-decoding errors precede tool restrictions. With common-only configuration validation, additional tool restrictions retain their existing precedence over a decoding error. Common-only function-result validation adds no native result restriction.

`engine.NewCatalog` validates every entry before publishing its immutable snapshot and panics with `engine.ErrInvalidDeclaration` for invalid static registrations. Kinds must be nonempty without surrounding whitespace. Placements must explicitly list at least one supported placement; MCP origins must be a non-nil list (an empty list qualifies none). Unknown or duplicate choices, origins without a corresponding placement and bearer support without an MCP origin are rejected. Errors identify authored fields without echoing declaration values. Future profile fields must be classified by the completeness validator and explicitly decided by every profile; there is no production default-filling constructor.

Run the `engine` and `execution` tests for omission, policy, combination and error precedence coverage, and the public onboarding/store tests for admission and Runtime dispatch. Test fixtures use `engine/enginetest`, whose exhaustive literal also requires a decision when a field is added; it is not a production profile.

`execution.Policy` supplies immutable service qualification to HTTP admission,
Worker device selection and final dispatch. Custom composition gives the same
Policy to `api.WithExecutionPolicy` and `Dispatcher.Policy`. The zero value uses
Expand Down
21 changes: 14 additions & 7 deletions services/core/internal/engine/claude.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,21 @@ import (
// providers reject text without non-whitespace characters.
func claudeProfile() Profile {
return Profile{
ProgrammaticToolCallingDisable: true,
ProgrammaticToolCallingDisable: proto.CapabilitySupported,
MCPOrigins: []string{"service", "environment"},
StructuredOutput: true,
ToolSearch: true,
MessageImages: true,
Placements: []string{"none", "openai_hosted", "self_hosted"}, MCPBearer: true,
ValidateConfiguration: validateClaudeConfiguration,
ValidateTools: validateClaudeTools,
Placements: []string{"none", "openai_hosted", "self_hosted"},
StructuredOutput: proto.CapabilitySupported,
ToolSearch: proto.CapabilitySupported,
MessageImages: proto.CapabilitySupported,
WhitespaceOnlyText: proto.CapabilityUnsupported,
WebSearchControl: proto.CapabilityUnsupported,
TextVerbosity: proto.CapabilityUnsupported,
MCPBearer: proto.CapabilitySupported,
ConfigurationValidation: AdditionalValidation,
ToolsValidation: AdditionalValidation,
FunctionResultValidation: AdditionalValidation,
ValidateConfiguration: validateClaudeConfiguration,
ValidateTools: validateClaudeTools,
ValidateFunctionResult: func(_ string, result proto.FunctionResultPayload) error {
for _, part := range result.Content {
if part.Type == "input_image" && !result.Success {
Expand Down
16 changes: 12 additions & 4 deletions services/core/internal/engine/codex.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,24 @@ package engine

import (
v1 "github.com/MiniMax-AI/OpenAgentCore/contracts/agents-api/v1"
"github.com/MiniMax-AI/OpenAgentCore/internal/agentdaemon/proto"
)

func codexProfile() Profile {
return Profile{
ProgrammaticToolCallingDisable: true,
ProgrammaticToolCallingDisable: proto.CapabilitySupported,
Placements: []string{"none", "self_hosted", "openai_hosted"},
MCPOrigins: []string{"service", "environment"},
MessageImages: true,
WhitespaceOnlyText: true,
WebSearchControl: true, TextVerbosity: true, MCPBearer: true,
MessageImages: proto.CapabilitySupported,
WhitespaceOnlyText: proto.CapabilitySupported,
WebSearchControl: proto.CapabilitySupported,
TextVerbosity: proto.CapabilitySupported,
MCPBearer: proto.CapabilitySupported,
StructuredOutput: proto.CapabilityUnsupported,
ToolSearch: proto.CapabilityUnsupported,
ConfigurationValidation: AdditionalValidation,
ToolsValidation: CommonValidationOnly,
FunctionResultValidation: CommonValidationOnly,
ValidateConfiguration: func(agent v1.Agent, _ *v1.Environment, _ bool) error {
return rejectSubagentTools(agent, "function", "mcp")
},
Expand Down
139 changes: 139 additions & 0 deletions services/core/internal/engine/declaration_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
package engine_test

import (
"errors"
"reflect"
"strconv"
"strings"
"testing"

v1 "github.com/MiniMax-AI/OpenAgentCore/contracts/agents-api/v1"
"github.com/MiniMax-AI/OpenAgentCore/internal/agentdaemon/proto"
"github.com/MiniMax-AI/OpenAgentCore/services/core/internal/engine"
"github.com/MiniMax-AI/OpenAgentCore/services/core/internal/engine/enginetest"
)

func requireDeclarationRejection(t *testing.T, p engine.Profile) {
t.Helper()
if err := p.ValidateDeclaration(); !errors.Is(err, engine.ErrInvalidDeclaration) {
t.Fatalf("expected declaration error, got %v", err)
}
defer func() {
err, ok := recover().(error)
if !ok || !errors.Is(err, engine.ErrInvalidDeclaration) {
t.Fatalf("catalog accepted invalid declaration: %v", err)
}
}()
engine.NewCatalog(map[string]engine.Profile{"fixture": p})
}

func TestEveryProfileFieldRequiresAnExplicitDecision(t *testing.T) {
// Claude exercises all three additional validators. Zeroing any field, even
// a future one, must fail before the profile enters an immutable catalog.
original, _ := (engine.Catalog{}).Lookup("claude_sdk")
typ := reflect.TypeOf(original)
for i := 0; i < typ.NumField(); i++ {
t.Run(typ.Field(i).Name, func(t *testing.T) {
p := original
reflect.ValueOf(&p).Elem().Field(i).SetZero()
requireDeclarationRejection(t, p)
})
}
}

func TestEveryServiceCapabilityRejectsInvalidValues(t *testing.T) {
for _, kind := range (engine.Catalog{}).Kinds() {
original, _ := (engine.Catalog{}).Lookup(kind)
if err := original.ValidateDeclaration(); err != nil {
t.Fatal(kind, err)
}
typ := reflect.TypeOf(original)
for i := 0; i < typ.NumField(); i++ {
if typ.Field(i).Type != reflect.TypeOf(proto.CapabilitySupport(0)) {
continue
}
for _, invalid := range []proto.CapabilitySupport{proto.CapabilityUnspecified, 3, 255} {
t.Run(kind+"/"+typ.Field(i).Name+"/"+strconv.Itoa(int(invalid)), func(t *testing.T) {
p := original
reflect.ValueOf(&p).Elem().Field(i).Set(reflect.ValueOf(invalid))
requireDeclarationRejection(t, p)
})
}
}
}
}

func TestValidationPoliciesMustMatchCallbacks(t *testing.T) {
original, _ := (engine.Catalog{}).Lookup("claude_sdk")
for _, field := range []string{"ConfigurationValidation", "ToolsValidation", "FunctionResultValidation"} {
for _, invalid := range []engine.ValidationPolicy{engine.ValidationUnspecified, 3, engine.CommonValidationOnly} {
t.Run(field+"/"+strconv.Itoa(int(invalid)), func(t *testing.T) {
p := original
reflect.ValueOf(&p).Elem().FieldByName(field).Set(reflect.ValueOf(invalid))
requireDeclarationRejection(t, p)
})
}
t.Run(field+"/missing callback", func(t *testing.T) {
p := enginetest.Profile(nil)
reflect.ValueOf(&p).Elem().FieldByName(field).Set(reflect.ValueOf(engine.AdditionalValidation))
requireDeclarationRejection(t, p)
})
}
// Common validation is sufficient when explicitly chosen, with no pretend
// successful callbacks. An empty MCP list is an explicit refusal of all origins.
if err := enginetest.Profile(nil).ValidateDeclaration(); err != nil {
t.Fatal(err)
}
}

func TestCatalogRejectsInvalidCombinationsBeforeCallbacks(t *testing.T) {
cases := map[string]func(*engine.Profile){
"empty placements": func(p *engine.Profile) { p.Placements = []string{} },
"unknown placement": func(p *engine.Profile) { p.Placements = []string{"private-fixture-value"} },
"duplicate placement": func(p *engine.Profile) { p.Placements = []string{"none", "none"} },
"unknown origin": func(p *engine.Profile) { p.MCPOrigins = []string{"private-fixture-value"} },
"duplicate origin": func(p *engine.Profile) { p.MCPOrigins = []string{"service", "service"} },
"service without placement": func(p *engine.Profile) { p.Placements = []string{"self_hosted"}; p.MCPOrigins = []string{"service"} },
"environment without placement": func(p *engine.Profile) { p.MCPOrigins = []string{"environment"} },
"bearer without MCP": func(p *engine.Profile) { p.MCPBearer = proto.CapabilitySupported },
}
for name, change := range cases {
t.Run(name, func(t *testing.T) {
p := enginetest.Profile(change)
p.ConfigurationValidation = engine.AdditionalValidation
p.ValidateConfiguration = func(v1.Agent, *v1.Environment, bool) error {
t.Fatal("registration invoked request validator")
return nil
}
if err := p.ValidateDeclaration(); err != nil && strings.Contains(err.Error(), "private-fixture-value") {
t.Fatal("declaration error disclosed input")
}
requireDeclarationRejection(t, p)
})
}
}

func TestCatalogRejectsEmptyProfileAndKind(t *testing.T) {
requireDeclarationRejection(t, engine.Profile{})
for _, kind := range []string{"", " ", " fixture"} {
t.Run("kind/"+kind, func(t *testing.T) {
defer func() {
if err, ok := recover().(error); !ok || !errors.Is(err, engine.ErrInvalidDeclaration) {
t.Fatal("invalid kind accepted", err)
}
}()
engine.NewCatalog(map[string]engine.Profile{kind: enginetest.Profile(nil)})
})
}
for _, profiles := range []map[string]engine.Profile{nil, {}} {
c := engine.NewCatalog(profiles)
if len(c.Kinds()) != 0 {
t.Fatal("empty catalog inherited builtins")
}
for _, kind := range (engine.Catalog{}).Kinds() {
if _, ok := c.Lookup(kind); ok {
t.Fatal("empty catalog authorized", kind)
}
}
}
}
32 changes: 32 additions & 0 deletions services/core/internal/engine/enginetest/profile.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Package enginetest supplies explicit declarations for admission fixtures.
// Production profiles must decide every field in their own constructors.
package enginetest

import (
"github.com/MiniMax-AI/OpenAgentCore/internal/agentdaemon/proto"
"github.com/MiniMax-AI/OpenAgentCore/services/core/internal/engine"
)

// Profile qualifies only common text admission by default. The literal is
// intentionally exhaustive; new fields stay unspecified until decided here.
func Profile(change func(*engine.Profile)) engine.Profile {
p := engine.Profile{
ProgrammaticToolCallingDisable: proto.CapabilityUnsupported,
WebSearchControl: proto.CapabilityUnsupported,
TextVerbosity: proto.CapabilityUnsupported,
MCPBearer: proto.CapabilityUnsupported,
StructuredOutput: proto.CapabilityUnsupported,
ToolSearch: proto.CapabilityUnsupported,
MessageImages: proto.CapabilityUnsupported,
WhitespaceOnlyText: proto.CapabilityUnsupported,
Placements: []string{"none"},
MCPOrigins: []string{},
ConfigurationValidation: engine.CommonValidationOnly,
ToolsValidation: engine.CommonValidationOnly,
FunctionResultValidation: engine.CommonValidationOnly,
}
if change != nil {
change(&p)
}
return p
}
44 changes: 29 additions & 15 deletions services/core/internal/engine/mcode.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,34 @@ import (
// WhitespaceOnlyText stays unqualified: the native runtime refuses such prompts
// with "Local message content or attachments are required.".
func mcodeProfile() Profile {
return Profile{MCPOrigins: []string{"environment"}, MCPBearer: true, ProgrammaticToolCallingDisable: true, Placements: []string{"none", "openai_hosted", "self_hosted"}, ValidateConfiguration: func(a v1.Agent, e *v1.Environment, daemon bool) error {
if e == nil || (e.Type != "none" && e.Type != "openai_hosted" && e.Type != "self_hosted") || daemon || strings.TrimSpace(a.Model) == "" || a.Reasoning.Effort != nil || a.Reasoning.Summary != nil || (a.ServiceTier != "" && a.ServiceTier != "auto") || (a.Text.Format.Type != "" && a.Text.Format.Type != "text") || (a.Text.Verbosity != "" && a.Text.Verbosity != "medium") {
return ErrInvalidInput
}
return rejectSubagentTools(a, "mcp")
}, ValidateTools: func(_ *v1.Environment, _ bool, functions []proto.FunctionTool, mcp []proto.MCPHTTPServer) error {
if len(functions) > 0 {
return errors.New("The configured engine does not support public functions.")
}
for _, server := range mcp {
if server.ServerLabel == "oac_workspace" || server.AllowedTools != nil || server.Required {
return errors.New("The configured engine requires an unreserved MCP label, allowed_tools=null and required=false.")
return Profile{
MCPOrigins: []string{"environment"},
Placements: []string{"none", "openai_hosted", "self_hosted"},
MCPBearer: proto.CapabilitySupported,
ProgrammaticToolCallingDisable: proto.CapabilitySupported,
StructuredOutput: proto.CapabilityUnsupported,
ToolSearch: proto.CapabilityUnsupported,
MessageImages: proto.CapabilityUnsupported,
WhitespaceOnlyText: proto.CapabilityUnsupported,
WebSearchControl: proto.CapabilityUnsupported,
TextVerbosity: proto.CapabilityUnsupported,
ConfigurationValidation: AdditionalValidation,
ToolsValidation: AdditionalValidation,
FunctionResultValidation: CommonValidationOnly,
ValidateConfiguration: func(a v1.Agent, e *v1.Environment, daemon bool) error {
if e == nil || (e.Type != "none" && e.Type != "openai_hosted" && e.Type != "self_hosted") || daemon || strings.TrimSpace(a.Model) == "" || a.Reasoning.Effort != nil || a.Reasoning.Summary != nil || (a.ServiceTier != "" && a.ServiceTier != "auto") || (a.Text.Format.Type != "" && a.Text.Format.Type != "text") || (a.Text.Verbosity != "" && a.Text.Verbosity != "medium") {
return ErrInvalidInput
}
}
return nil
}}
return rejectSubagentTools(a, "mcp")
}, ValidateTools: func(_ *v1.Environment, _ bool, functions []proto.FunctionTool, mcp []proto.MCPHTTPServer) error {
if len(functions) > 0 {
return errors.New("The configured engine does not support public functions.")
}
for _, server := range mcp {
if server.ServerLabel == "oac_workspace" || server.AllowedTools != nil || server.Required {
return errors.New("The configured engine requires an unreserved MCP label, allowed_tools=null and required=false.")
}
}
return nil
}}
}
18 changes: 12 additions & 6 deletions services/core/internal/engine/mcp_test.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
package engine
package engine_test

import (
"testing"

v1 "github.com/MiniMax-AI/OpenAgentCore/contracts/agents-api/v1"
"github.com/MiniMax-AI/OpenAgentCore/internal/agentdaemon/proto"
"testing"
"github.com/MiniMax-AI/OpenAgentCore/services/core/internal/engine"
"github.com/MiniMax-AI/OpenAgentCore/services/core/internal/engine/enginetest"
)

func TestMCPOriginQualification(t *testing.T) {
for _, kind := range []string{"codex", "claude_sdk", "mcode"} {
profile, ok := (Catalog{}).Lookup(kind)
profile, ok := (engine.Catalog{}).Lookup(kind)
if !ok {
t.Fatal(kind)
}
Expand All @@ -27,7 +30,7 @@ func TestMCPOriginQualification(t *testing.T) {
}
}
func TestMiniMaxMCPPoliciesRejectInsteadOfDropping(t *testing.T) {
p, _ := (Catalog{}).Lookup("mcode")
p, _ := (engine.Catalog{}).Lookup("mcode")
empty := []string{}
named := []string{"proof"}
for _, allowed := range []*[]string{nil, &empty, &named} {
Expand All @@ -41,8 +44,11 @@ func TestMiniMaxMCPPoliciesRejectInsteadOfDropping(t *testing.T) {
}
}
func TestMCPOriginCatalogIsImmutable(t *testing.T) {
p := Profile{MCPOrigins: []string{"environment"}}
c := NewCatalog(map[string]Profile{"fixture": p})
p := enginetest.Profile(func(p *engine.Profile) {
p.MCPOrigins = []string{"environment"}
p.Placements = []string{"self_hosted"}
})
c := engine.NewCatalog(map[string]engine.Profile{"fixture": p})
p.MCPOrigins[0] = "service"
first, _ := c.Lookup("fixture")
if first.MCPOrigins[0] != "environment" {
Expand Down
Loading