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
2 changes: 1 addition & 1 deletion contracts/agents-api/harness-onboarding.md
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ Before starting, record the operation set, expected results, exclusions and stop

1. **Contract tests.** Call `agent/contracttest.TextLifecycle` from a test named `TestSharedTextLifecycle` with the adapter's prepared Executor and a deterministic native fixture; `claudesdk/executor_test.go` is the reference. It checks independent Turn streams, native owner and history continuity, durable write and application receipts, stale cancellation and healthy continuation after cancellation. `make check-runtime-contract` runs it together with the shared wire, gateway, transport and dispatcher tests, the declaration completeness check and each adapter's `TestUnsupportedExtensionsHaveNoNativeEffects`. Adapter tests also cover two ordinary Turns sharing one native process or connection and history, cancellation followed by another Turn, stale cancellation and late events, native exit, cleanup failure, input write and application receipts, unknown outcomes and fresh per-Turn usage, function, input and child-observation state. State whether a fixture is controlled or a real provider.
2. **Shared integration.** `TestThirdHarnessPublicOnboarding` runs the synthetic Harness through public Session and input admission, Worker device selection, the real WebSocket gateway, the daemon Registry and Router, neutral events and durable terminal projection. It uses a custom immutable `engine.Catalog` in the same `execution.Policy` given to the API handler and the dispatcher, and checks applied input receipts, saved native identity, continuation, cancellation, unsupported optional requests and missing mandatory Runtime support. The fixture has no workspace, MCP, public functions, permissions or user-choice handlers, and its registration stays local to the test. It proves the integration path, not native execution.
3. **Real acceptance.** Use the pinned official Python SDK and raw HTTP against Core, a real provider API, the native Harness and a dedicated database. Verify initial execution, a warm follow-up, cancellation and restart with continuation; record native owner identity and same-condition cold and warm timing. For workspace placements also verify Files and Artifacts, workspace identity, that no credentials appear in public responses and that foreign history is rejected. `services/core/tests/official_hosted_functions_native.py` holds the shared function assertions: success and error, native file output and public Artifact bytes, same-history continuation after restart, foreign result rejection and pending-call cancellation. Synthetic or failed runs never count. The opt-in tests below run the pinned-SDK fixtures in `services/core/tests` against a real daemon and model; each runs when `OAC_TEST_OFFICIAL_SDK_PYTHON`, `OAC_TEST_NATIVE_DAEMON_BIN`, `OAC_TEST_NATIVE_PROOF_DIR` and its private options file are set.
3. **Real acceptance.** Use the pinned official Python SDK and raw HTTP against Core, a real provider API, the native Harness and a dedicated database. Verify initial execution, a warm follow-up, cancellation and restart with continuation; record native owner identity and same-condition cold and warm timing. For workspace placements also verify Files and Artifacts, workspace identity, that no credentials appear in public responses and that foreign history is rejected. `services/core/tests/official_hosted_functions_native.py` holds the shared function assertions: success and error, native file output and public Artifact bytes, same-history continuation after restart, foreign result rejection and pending-call cancellation. Synthetic or failed runs never count. The opt-in tests below run the pinned-SDK fixtures in `services/core/tests` against a real daemon and model; each runs when `OAC_TEST_OFFICIAL_SDK_PYTHON`, `OAC_TEST_NATIVE_DAEMON_BIN`, `OAC_TEST_NATIVE_PROOF_DIR` and its private options file are set. The options file is a JSON object with exactly `model` and `model_provider` (the fields of `x_agents_core.model_provider`); the test sets it as the deployment default model provider, which the fixtures' `environment: none` Sessions freeze at creation.
4. **Regression.** Existing Harnesses keep working. Run targeted tests, then `make check`; run `make openapi` after API changes and `make sqlc-generate` after query changes.
5. **Review.** Follow the [blind review workflow](../../CONTRIBUTING.md#review).

Expand Down
2 changes: 1 addition & 1 deletion services/core/internal/api/session_environment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const environmentOrigin = "wss://core.example/api/v1/agent-daemon/ws"
func environmentSession() store.Session {
return store.Session{
ID: "session", TenantID: "tenant", CreatedAt: time.Unix(1700000000, 0), Metadata: map[string]string{},
Configuration: json.RawMessage(`{"agent":{"id":"agent_test","model":"model","tools":[]},"environment":{"type":"self_hosted"},"daemon":{"credential":"private"}}`),
Configuration: json.RawMessage(`{"agent":{"id":"agent_test","model":"model","tools":[]},"environment":{"type":"self_hosted"}}`),
Environment: &store.Environment{
ID: "environment", SessionID: "session", TenantID: "tenant", Status: "pending",
Configuration: json.RawMessage(`{"type":"self_hosted","workspace_directory":"/remote/workspace","capability_directories":["/remote/capabilities"],"id":"forged","remote_url":"https://secret@private","env":{"SECRET":"private"},"setup_commands":["private"]}`),
Expand Down
6 changes: 3 additions & 3 deletions services/core/internal/engine/claude.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ func claudeProfile() Profile {
}
}

func validateClaudeConfiguration(agent v1.Agent, environment *v1.Environment, hasDaemon bool) error {
if environment == nil || (environment.Type != "none" && environment.Type != "openai_hosted" && environment.Type != "self_hosted") || hasDaemon || strings.TrimSpace(agent.Model) == "" {
func validateClaudeConfiguration(agent v1.Agent, environment *v1.Environment) error {
if environment == nil || (environment.Type != "none" && environment.Type != "openai_hosted" && environment.Type != "self_hosted") || strings.TrimSpace(agent.Model) == "" {
return ErrInvalidInput
}
if agent.Text.Verbosity != "" && agent.Text.Verbosity != "medium" {
Expand Down Expand Up @@ -90,7 +90,7 @@ func validateClaudeConfiguration(agent v1.Agent, environment *v1.Environment, ha
return rejectSubagentTools(agent, "function", "mcp")
}

func validateClaudeTools(environment *v1.Environment, _ bool, tools []proto.FunctionTool, mcp []proto.MCPHTTPServer) error {
func validateClaudeTools(environment *v1.Environment, tools []proto.FunctionTool, mcp []proto.MCPHTTPServer) error {
if err := validateClaudeMCP(mcp); err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion services/core/internal/engine/codex.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ func codexProfile() Profile {
ConfigurationValidation: AdditionalValidation,
ToolsValidation: CommonValidationOnly,
FunctionResultValidation: CommonValidationOnly,
ValidateConfiguration: func(agent v1.Agent, _ *v1.Environment, _ bool) error {
ValidateConfiguration: func(agent v1.Agent, _ *v1.Environment) error {
return rejectSubagentTools(agent, "function", "mcp")
},
}
Expand Down
2 changes: 1 addition & 1 deletion services/core/internal/engine/declaration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ func TestCatalogRejectsInvalidCombinationsBeforeCallbacks(t *testing.T) {
t.Run(name, func(t *testing.T) {
p := enginetest.Profile(change)
p.ConfigurationValidation = engine.AdditionalValidation
p.ValidateConfiguration = func(v1.Agent, *v1.Environment, bool) error {
p.ValidateConfiguration = func(v1.Agent, *v1.Environment) error {
t.Fatal("registration invoked request validator")
return nil
}
Expand Down
6 changes: 3 additions & 3 deletions services/core/internal/engine/mcode.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@ func mcodeProfile() Profile {
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") {
ValidateConfiguration: func(a v1.Agent, e *v1.Environment) error {
if e == nil || (e.Type != "none" && e.Type != "openai_hosted" && e.Type != "self_hosted") || 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 {
}, ValidateTools: func(_ *v1.Environment, functions []proto.FunctionTool, mcp []proto.MCPHTTPServer) error {
if len(functions) > 0 {
return errors.New("The configured engine does not support public functions.")
}
Expand Down
6 changes: 3 additions & 3 deletions services/core/internal/engine/mcp.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@ import (

// ValidateMCPOrigins preserves outbound authority independently of Harness names.
// Workspace connections never stand in for service-network connections.
func (p Profile) ValidateMCPOrigins(environment *v1.Environment, hasDaemon bool, servers []proto.MCPHTTPServer) error {
func (p Profile) ValidateMCPOrigins(environment *v1.Environment, servers []proto.MCPHTTPServer) error {
for _, server := range servers {
switch server.ConnectionOrigin {
case "service":
if environment == nil || environment.Type != "none" || hasDaemon {
if environment == nil || environment.Type != "none" {
return errors.New("Service-origin MCP requires the service-side environment:none profile.")
}
case "environment":
if environment == nil || (environment.Type != "openai_hosted" && environment.Type != "self_hosted") || hasDaemon {
if environment == nil || (environment.Type != "openai_hosted" && environment.Type != "self_hosted") {
return errors.New("Environment-origin MCP requires a managed or self-hosted execution Environment.")
}
default:
Expand Down
7 changes: 2 additions & 5 deletions services/core/internal/engine/mcp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,9 @@ func TestMCPOriginQualification(t *testing.T) {
for _, origin := range []string{"service", "environment", "", "unknown"} {
servers := []proto.MCPHTTPServer{{ConnectionOrigin: origin, ServerLabel: "proof", ServerURL: "https://example.test/mcp"}}
allowed := origin == "environment" && placement != "none" || origin == "service" && placement == "none" && kind != "mcode"
if err := profile.ValidateMCPOrigins(&v1.Environment{Type: placement}, false, servers); (err == nil) != allowed {
if err := profile.ValidateMCPOrigins(&v1.Environment{Type: placement}, servers); (err == nil) != allowed {
t.Fatalf("%s/%s/%s: %v", kind, placement, origin, err)
}
if profile.ValidateMCPOrigins(&v1.Environment{Type: placement}, true, servers) == nil {
t.Fatal("legacy daemon placement admitted")
}
}
}
}
Expand All @@ -36,7 +33,7 @@ func TestMiniMaxMCPPoliciesRejectInsteadOfDropping(t *testing.T) {
for _, allowed := range []*[]string{nil, &empty, &named} {
for _, required := range []bool{false, true} {
server := proto.MCPHTTPServer{ConnectionOrigin: "environment", ServerLabel: "proof", ServerURL: "https://example.test", AllowedTools: allowed, Required: required}
err := p.ValidateTools(&v1.Environment{Type: "self_hosted"}, false, nil, []proto.MCPHTTPServer{server})
err := p.ValidateTools(&v1.Environment{Type: "self_hosted"}, nil, []proto.MCPHTTPServer{server})
if (err == nil) != (allowed == nil && !required) {
t.Fatal("unsupported MCP policy accepted", err)
}
Expand Down
4 changes: 2 additions & 2 deletions services/core/internal/engine/profile.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ type Profile struct {
ConfigurationValidation ValidationPolicy
ToolsValidation ValidationPolicy
FunctionResultValidation ValidationPolicy
ValidateConfiguration func(agent v1.Agent, environment *v1.Environment, hasDaemon bool) error
ValidateTools func(environment *v1.Environment, hasDaemon bool, functions []proto.FunctionTool, mcp []proto.MCPHTTPServer) error
ValidateConfiguration func(agent v1.Agent, environment *v1.Environment) error
ValidateTools func(environment *v1.Environment, functions []proto.FunctionTool, mcp []proto.MCPHTTPServer) error
ValidateFunctionResult func(placement string, result proto.FunctionResultPayload) error
// WhitespaceOnlyText qualifies messages without an image or non-whitespace
// text. Unqualified harnesses reject them at admission; Core never trims or
Expand Down
6 changes: 3 additions & 3 deletions services/core/internal/engine/subagents_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ func TestSubagentProfilesPreserveQualifiedOperationBoundaries(t *testing.T) {
agent := v1.Agent{Model: "real-model"}
agent.MultiAgent.Enabled = true
for _, placement := range []string{"none", "openai_hosted", "self_hosted"} {
if err := profile.ValidateConfiguration(agent, &v1.Environment{Type: placement}, false); err != nil {
if err := profile.ValidateConfiguration(agent, &v1.Environment{Type: placement}); err != nil {
t.Fatal(placement, err)
}
}
Expand All @@ -28,11 +28,11 @@ func TestSubagentProfilesPreserveQualifiedOperationBoundaries(t *testing.T) {
} // Its existing tool profile rejects both for every Session.
for _, tool := range []string{`{"type":"function","name":"f"}`, `{"type":"mcp","server_label":"s"}`} {
agent.Tools = []json.RawMessage{json.RawMessage(tool)}
if err := profile.ValidateConfiguration(agent, &v1.Environment{Type: "none"}, false); err == nil {
if err := profile.ValidateConfiguration(agent, &v1.Environment{Type: "none"}); err == nil {
t.Fatal("unqualified multi-agent combination accepted", tool)
}
agent.MultiAgent.Enabled = false
if err := profile.ValidateConfiguration(agent, &v1.Environment{Type: "none"}, false); err != nil {
if err := profile.ValidateConfiguration(agent, &v1.Environment{Type: "none"}); err != nil {
t.Fatal("single-agent profile changed", err)
}
agent.MultiAgent.Enabled = true
Expand Down
20 changes: 4 additions & 16 deletions services/core/internal/execution/dispatcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,31 +14,20 @@ import (
"github.com/MiniMax-AI/OpenAgentCore/services/core/internal/store"
)

// Snapshot is resolved internally; Daemon is not a public environment wire type.
// Snapshot is the Session configuration frozen at creation.
type Snapshot struct {
ModelProviderConfigured bool `json:"model_provider_configured,omitempty"`
Agent v1.Agent `json:"agent"`
Daemon *DaemonConfig `json:"daemon"`
Environment *v1.Environment `json:"environment"`
VaultIDs []string `json:"vault_ids,omitempty"`
MCPCredentials []store.MCPCredentialBinding `json:"mcp_credentials,omitempty"`
}

type DaemonConfig struct {
WorkDir string `json:"work_dir"`
}

type Dispatcher struct {
notifications *executionNotifications
Policy
Store *store.Store
Registry *runtimegateway.Registry
// Options optionally supplies native adapter options for Sessions that need
// no frozen model provider (environment none and legacy daemon Sessions).
// The server command leaves it nil since the operator options file was
// retired; native tests use it to reach synthetic model servers. It is never
// consulted for openai_hosted or self_hosted Sessions.
Options func(context.Context, store.Session) (map[string]any, error)
// ManagedRuntimes is optional internal provisioning; it does not admit hosted API requests.
ManagedRuntimes *RuntimeProvider
// MaxConcurrentExecutions bounds work admitted by this Core execution owner.
Expand Down Expand Up @@ -77,9 +66,8 @@ func (d *Dispatcher) Run(ctx context.Context, tenantID, sessionID, turnID string
if err != nil {
return store.Turn{}, err
}
workDir, noEnvironment, err := resolveExecutionEnvironment(snapshot)
if err != nil {
return store.Turn{}, err
if !environmentNone(snapshot) {
return store.Turn{}, store.ErrInvalidInput
}
text, through, err := d.initialInput(ctx, tenantID, sessionID, turnID)
if err != nil {
Expand All @@ -92,7 +80,7 @@ func (d *Dispatcher) Run(ctx context.Context, tenantID, sessionID, turnID string
if err != nil {
return store.Turn{}, err
}
req.WorkDir, req.DisableExecutionEnvironment = workDir, noEnvironment
req.DisableExecutionEnvironment = true
prepared, err := d.prepareTurnExecutor(ctx, peer, tenantID, sessionID, turnID, req, store.TurnQueued)
if err != nil {
return store.Turn{}, err
Expand Down
6 changes: 3 additions & 3 deletions services/core/internal/execution/engine_profile.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ func validateProfileConfiguration(profile engine.Profile, snapshot Snapshot) err
return errors.New("Structured output is not qualified for this engine.")
}
if profile.ConfigurationValidation == engine.AdditionalValidation {
if err := profile.ValidateConfiguration(snapshot.Agent, snapshot.Environment, snapshot.Daemon != nil); err != nil {
if err := profile.ValidateConfiguration(snapshot.Agent, snapshot.Environment); err != nil {
return profileError(err)
}
}
Expand All @@ -31,7 +31,7 @@ func validateProfileConfiguration(profile engine.Profile, snapshot Snapshot) err
return err
}
if err == nil {
if err := profile.ValidateMCPOrigins(snapshot.Environment, snapshot.Daemon != nil, tools.MCP); err != nil {
if err := profile.ValidateMCPOrigins(snapshot.Environment, tools.MCP); err != nil {
return err
}
if tools.DisableProgrammatic && !profile.ProgrammaticToolCallingDisable.IsSupported() {
Expand All @@ -43,7 +43,7 @@ func validateProfileConfiguration(profile engine.Profile, snapshot Snapshot) err
}
}
if profile.ToolsValidation == engine.AdditionalValidation {
if validationErr := profile.ValidateTools(snapshot.Environment, snapshot.Daemon != nil, tools.Functions, tools.MCP); validationErr != nil {
if validationErr := profile.ValidateTools(snapshot.Environment, tools.Functions, tools.MCP); validationErr != nil {
return profileError(validationErr)
}
}
Expand Down
10 changes: 5 additions & 5 deletions services/core/internal/execution/engine_profile_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,14 @@ func TestAdditionalProfileUsesCommonAdmission(t *testing.T) {
profile.ConfigurationValidation = engine.AdditionalValidation
profile.ToolsValidation = engine.AdditionalValidation
profile.FunctionResultValidation = engine.AdditionalValidation
profile.ValidateConfiguration = func(agent v1.Agent, environment *v1.Environment, hasDaemon bool) error {
profile.ValidateConfiguration = func(agent v1.Agent, environment *v1.Environment) error {
configurationChecked = true
if agent.Model != "fixture" || environment == nil || hasDaemon {
if agent.Model != "fixture" || environment == nil {
return engine.ErrInvalidInput
}
return nil
}
profile.ValidateTools = func(_ *v1.Environment, _ bool, tools []proto.FunctionTool, mcp []proto.MCPHTTPServer) error {
profile.ValidateTools = func(_ *v1.Environment, tools []proto.FunctionTool, mcp []proto.MCPHTTPServer) error {
toolsChecked = true
if len(tools) != 1 || tools[0].Name != "echo" || len(mcp) != 0 {
t.Fatal("common tool decoding did not reach profile")
Expand Down Expand Up @@ -105,15 +105,15 @@ func TestExplicitValidationPoliciesPreserveErrorPrecedence(t *testing.T) {
profile := enginetest.Profile(func(p *engine.Profile) {
p.ConfigurationValidation = configuration
if configuration == engine.AdditionalValidation {
p.ValidateConfiguration = func(v1.Agent, *v1.Environment, bool) error {
p.ValidateConfiguration = func(v1.Agent, *v1.Environment) error {
if rejectConfiguration {
return configurationErr
}
return nil
}
}
p.ToolsValidation = engine.AdditionalValidation
p.ValidateTools = func(*v1.Environment, bool, []proto.FunctionTool, []proto.MCPHTTPServer) error {
p.ValidateTools = func(*v1.Environment, []proto.FunctionTool, []proto.MCPHTTPServer) error {
toolsCalled = true
return toolsErr
}
Expand Down
Loading
Loading