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
35 changes: 14 additions & 21 deletions apps/daemon/internal/agent/claudesdk/declaration.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,34 +100,27 @@ func discoverWithCheck(parent context.Context, options agent.DiscoveryOptions, d
if err != nil {
return fail(err)
}
caps := &out.Info.Capabilities
if config.Workspace != nil {
if !info.SupportsLocalRuntime() {
return fail(fmt.Errorf("Claude SDK bundle does not support the local Runtime contract"))
}
caps := &out.Info.Capabilities
caps.EnvironmentNone, caps.FunctionTools = proto.CapabilityUnsupported, proto.CapabilityFromBool(info.SupportsWorkspaceFunctions())
caps.LocalEnvironment = proto.CapabilitySupported
caps.NativeSessionRecovery = proto.CapabilitySupported
}
// One declaration holds for every Executor of the install: the workspace
// bridge, the agent-host view and a Runtime without a workspace, so each
// feature is its workspace variant, which the others also support.
out.Info.Available, out.Info.Version = true, info.SDK
out.Info.Capabilities.MessageImages = proto.CapabilityFromBool(info.SupportsMessageImages())
out.Info.Capabilities.FunctionResultImages = proto.CapabilityFromBool(info.SupportsFunctionResultImages())
out.Info.Capabilities.ToolSearch = proto.CapabilityFromBool(info.SupportsToolSearch())
if config.Workspace != nil {
out.Info.Capabilities.ToolSearch = proto.CapabilityFromBool(info.SupportsWorkspaceToolSearch())
}
out.Info.Capabilities.StructuredOutput = proto.CapabilityFromBool(info.SupportsStructuredOutput())
if config.Workspace != nil {
out.Info.Capabilities.StructuredOutput = proto.CapabilityFromBool(info.SupportsWorkspaceStructuredOutput())
}
out.Info.Capabilities.SubagentObservations = proto.CapabilityFromBool(info.SupportsSubagents())
out.Info.Capabilities.MCPHTTPTools = proto.CapabilityFromBool(info.SupportsHTTPMCP())
out.Info.Capabilities.MCPHTTPBearerAuth = proto.CapabilityFromBool(info.SupportsHTTPMCPBearer())
out.Info.Capabilities.MCPHTTPRequired = proto.CapabilityFromBool(info.SupportsHTTPMCPRequired())
if config.Workspace != nil && !info.SupportsWorkspaceMCP() {
out.Info.Capabilities.MCPHTTPTools, out.Info.Capabilities.MCPHTTPBearerAuth = proto.CapabilityUnsupported, proto.CapabilityUnsupported
out.Info.Capabilities.MCPHTTPRequired = proto.CapabilityUnsupported
}
caps.LocalEnvironment = proto.CapabilityFromBool(info.SupportsLocalRuntime())
caps.FunctionTools = proto.CapabilityFromBool(info.SupportsWorkspaceFunctions())
caps.MessageImages = proto.CapabilityFromBool(info.SupportsMessageImages())
caps.FunctionResultImages = proto.CapabilityFromBool(info.SupportsFunctionResultImages())
caps.ToolSearch = proto.CapabilityFromBool(info.SupportsWorkspaceToolSearch())
caps.StructuredOutput = proto.CapabilityFromBool(info.SupportsWorkspaceStructuredOutput())
caps.SubagentObservations = proto.CapabilityFromBool(info.SupportsSubagents())
caps.MCPHTTPTools = proto.CapabilityFromBool(info.SupportsWorkspaceMCP())
caps.MCPHTTPBearerAuth = proto.CapabilityFromBool(info.SupportsWorkspaceMCP() && info.SupportsHTTPMCPBearer())
caps.MCPHTTPRequired = proto.CapabilityFromBool(info.SupportsWorkspaceMCP() && info.SupportsHTTPMCPRequired())
out.Executor = NewExecutorFactory(config)
// The view runs the same install; its probe stays on this host.
if view, err := newView(Config{Node: node, Entrypoint: entrypoint}, info); err != nil {
Expand Down
4 changes: 3 additions & 1 deletion apps/daemon/internal/agent/claudesdk/declaration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,9 @@ func TestClaudeSDKFeatureDiscovery(t *testing.T) {
t.Setenv(claudeSDKNodeEnv, node)
for _, features := range [][]string{nil, {"mcp_http_tools"}, {"mcp_http_bearer_auth"}, {"mcp_http_tools", "mcp_http_bearer_auth"}, {"mcp_http_required"}, {"mcp_http_tools", "mcp_http_required"}, {"subagent_resources"}, {"structured_output"}} {
out := discoverWithCheck(t.Context(), agent.DiscoveryOptions{Profile: "default", Stdout: &strings.Builder{}, Stderr: &strings.Builder{}}, Declaration.Info, func(context.Context, Config) (RuntimeInfo, error) {
info := RuntimeInfo{SDK: "0.3.269", Native: "2.1.269 (Claude Code)", Features: features}
// The bundle's workspace variants, which the declaration uses.
workspace := []string{"workspace_tools", "workspace_prepare", "workspace_command_observations", "local_runtime_v2", "workspace_mcp_http", "workspace_structured_output"}
info := RuntimeInfo{SDK: "0.3.269", Native: "2.1.269 (Claude Code)", Features: append(slices.Clone(features), workspace...)}
return info, nil
})
supported := len(features) > 0 && features[0] == "mcp_http_tools"
Expand Down
8 changes: 0 additions & 8 deletions apps/daemon/internal/agent/claudesdk/view.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,14 +83,6 @@ func declareView(probe Config, info RuntimeInfo, node, root, bridge string, load
Shims: []string{"bash", "rg", "git"},
ForwardEnv: []string{"CLAUDECODE", "GIT_EDITOR"},
Proxy: agent.ViewProxyEnv,
Capabilities: agent.ViewCapabilities{
EnvironmentNone: proto.CapabilitySupported,
Skills: proto.CapabilityUnsupported,
FunctionTools: proto.CapabilityFromBool(info.SupportsWorkspaceFunctions()),
FunctionResultImages: proto.CapabilityFromBool(info.SupportsFunctionResultImages()),
ToolSearch: proto.CapabilityFromBool(info.SupportsWorkspaceToolSearch()),
StdioMCP: proto.CapabilitySupported,
},
}
loader.AddTo(view)
view.Executor = newViewExecutorFactory(probe, layout)
Expand Down
2 changes: 1 addition & 1 deletion apps/daemon/internal/agent/claudesdk/view_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ func resolveTestView(t *testing.T) agent.View {
loader := viewloader.Fragment{Closure: []agent.ViewMount{lib}, Overlays: []agent.ViewOverlay{{Path: "/lib64/ld-linux-x86-64.so.2", Source: filepath.Join(root, "lib", "ld.so"), Exec: true}}, LibraryPath: lib.Path()}
declared := declareView(probe, RuntimeInfo{NativePath: "native/claude"}, probe.Node, filepath.Join(root, "bundle"), "dist/main.js", loader)
registry := agent.NewRegistry()
registry.Register(Declaration, agent.Runtime{Info: Declaration.Info, View: declared})
registry.Register(Declaration, agent.Runtime{Info: Declaration.Info, View: declared}, agent.EnvironmentSupport{Local: true, None: true})
view, err := registry.ResolveView(Declaration.Info.Kind)
if err != nil {
t.Fatal(err)
Expand Down
3 changes: 2 additions & 1 deletion apps/daemon/internal/agent/codex/declaration.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ func discoverWithCheck(parent context.Context, options agent.DiscoveryOptions, i
runtime.Info.Available, runtime.Info.Version = true, version
caps := &runtime.Info.Capabilities
caps.NativeSessionRecovery = proto.CapabilityFromBool(SupportsNativeSessionRecovery(version))
caps.LocalEnvironment = proto.CapabilityFromBool(SupportsLocalEnvironment(version))
// A local Environment requires native Session recovery.
caps.LocalEnvironment = caps.NativeSessionRecovery
caps.MCPHTTPRequired = proto.CapabilityFromBool(SupportsNativeSessionRecovery(version))
runtime.Executor = NewExecutorFactory()
runtime.View = discoverView(version)
Expand Down
2 changes: 1 addition & 1 deletion apps/daemon/internal/agent/codex/declaration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ func TestMCPRequiredDiscoveryRequiresPinnedNative(t *testing.T) {
for _, version := range []string{"codex-cli 0.153.4", "codex-cli 0.153.3", "codex-cli 0.154.0"} {
runtime := discoverWithCheck(t.Context(), agent.DiscoveryOptions{Stdout: io.Discard, Stderr: io.Discard}, Declaration.Info, func(context.Context, string) (string, error) { return version, nil })

if !runtime.Info.Available || runtime.Executor == nil || runtime.Info.Capabilities.LocalEnvironment.IsSupported() != SupportsLocalEnvironment(version) {
if !runtime.Info.Available || runtime.Executor == nil {
t.Fatalf("factories: %+v", runtime)
}
if runtime.Info.Capabilities.MCPHTTPRequired.IsSupported() != (version == "codex-cli 0.153.4") {
Expand Down
16 changes: 0 additions & 16 deletions apps/daemon/internal/agent/codex/environment_local.go

This file was deleted.

8 changes: 0 additions & 8 deletions apps/daemon/internal/agent/codex/view.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,14 +86,6 @@ func newView(binary string, codeModeHost bool) agent.View {
ShimPaths: []string{"/bin/bash"},
ForwardEnv: slices.Clone(viewForwardEnv),
Proxy: agent.ViewProxyEnv,
Capabilities: agent.ViewCapabilities{
EnvironmentNone: proto.CapabilitySupported,
Skills: proto.CapabilityUnsupported,
FunctionTools: proto.CapabilitySupported,
FunctionResultImages: proto.CapabilitySupported,
ToolSearch: proto.CapabilityUnsupported,
StdioMCP: proto.CapabilitySupported,
},
Executor: func(ctx context.Context, req proto.PromptRequestPayload, session agent.ViewSession) (agent.Executor, error) {
cfg := defaultSessionConfig()
cfg.codexBinary = binary
Expand Down
2 changes: 1 addition & 1 deletion apps/daemon/internal/agent/codex/view_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ func TestViewExecutorLaunchesInTheSessionView(t *testing.T) {
info := Declaration.Info
info.Available = true
registry := agent.NewRegistry()
registry.Register(Declaration, agent.Runtime{Info: info, View: &declared})
registry.Register(Declaration, agent.Runtime{Info: info, View: &declared}, agent.EnvironmentSupport{Local: true, None: true})
view, err := registry.ResolveView("codex")
if err != nil {
t.Fatal(err)
Expand Down
63 changes: 30 additions & 33 deletions apps/daemon/internal/agent/harness.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@
// static declaration list and installs each resulting Runtime through Register.
// Availability and factory selection belong to the adapter. RegisterKind resets
// the factories, so Register installs it first. The Runtime's Environment
// owner, not the adapter, serves and declares workspace operations.
// owner, not the adapter, serves and declares the Environments a kind runs
// in: Register composes its EnvironmentSupport with the Harness's own
// declaration once.
//
// Runtime registration and Core service qualification remain separate. A public
// Harness also needs a profile in services/core/internal/engine; advertising
Expand Down Expand Up @@ -70,14 +72,36 @@ type Runtime struct {
View *View
}

// Register installs a discovered Runtime with its declaration's configuration.
func (r *Registry) Register(declaration Declaration, runtime Runtime) {
// EnvironmentSupport is what the Runtime's Environment owner serves. The
// Harness's own declaration states LocalEnvironment and EnvironmentNone as
// what its Executors can run; the owner decides which of them the Runtime
// offers, and serves read-only preparation and output export wherever it
// offers a local Environment.
type EnvironmentSupport struct {
// Local serves executions in a local Environment.
Local bool
// None serves executions with environment none.
None bool
}

// Compose narrows caps, a Harness's own declaration, to what s serves.
func (s EnvironmentSupport) Compose(caps proto.AgentKindCapabilities) proto.AgentKindCapabilities {
caps.LocalEnvironment = proto.CapabilityFromBool(s.Local && caps.LocalEnvironment.IsSupported())
caps.WorkspaceReadPreparation, caps.WorkspaceOutputExport = caps.LocalEnvironment, caps.LocalEnvironment
caps.EnvironmentNone = proto.CapabilityFromBool(s.None && caps.EnvironmentNone.IsSupported())
return caps
}

// Register installs a discovered Runtime with its declaration's configuration,
// in the Environments that environments serves.
func (r *Registry) Register(declaration Declaration, runtime Runtime, environments EnvironmentSupport) {
if runtime.Info.Kind != declaration.Info.Kind {
panic("agent.Registry.Register: discovery kind differs from declaration")
}
if !runtime.Info.Available && runtime.Executor != nil {
panic("agent.Registry.Register: unavailable runtime has factories")
}
runtime.Info.Capabilities = environments.Compose(runtime.Info.Capabilities)
r.RegisterKind(runtime.Info, declaration.Configuration)
if runtime.Executor != nil {
r.RegisterExecutor(runtime.Info.Kind, runtime.Executor)
Expand All @@ -91,8 +115,8 @@ func (r *Registry) Register(declaration Declaration, runtime Runtime) {
// view: the sandbox world at /, the closure, home and shims under
// ViewPrivateRoot, and a loopback-only network whose model, MCP and proxy
// endpoints belong to the Session's credential gateway. The declaration is
// data; the agent host builds each view from it and the Session, and admits a
// request only when the view declares each capability the request uses.
// data; the agent host builds each view from it and the Session. A view runs
// every request that the Runtime's declaration admits.
//
// Environment none. A request with DisableExecutionEnvironment runs in an
// empty-root view: a read-only, noexec tmpfs root that holds only the
Expand Down Expand Up @@ -207,28 +231,7 @@ type View struct {
// environment wins over a forwarded variable of the same name.
ForwardEnv []string
Proxy ViewProxy
// Capabilities declares what the view supports.
Capabilities ViewCapabilities
Executor ViewExecutorFactory
}

// ViewCapabilities declares, field by field, what a view supports. Each field
// is set explicitly.
type ViewCapabilities struct {
// EnvironmentNone runs a request with DisableExecutionEnvironment in an
// empty-root view.
EnvironmentNone proto.CapabilitySupport
// Skills runs a request with resolved Skills (LocalEnvironment.Skills).
Skills proto.CapabilitySupport
// FunctionTools, FunctionResultImages and ToolSearch mean what the
// proto.AgentKindCapabilities fields of the same names mean.
FunctionTools proto.CapabilitySupport
FunctionResultImages proto.CapabilitySupport
ToolSearch proto.CapabilitySupport
// StdioMCP runs stdio MCP bindings under their aliases. A stdio binding
// whose CredentialAuthority is not "none" is rejected with ErrViewHandoff
// whatever the view declares.
StdioMCP proto.CapabilitySupport
Executor ViewExecutorFactory
}

// ViewMount presents HostDir at ViewPrivateRoot/<Name>.
Expand Down Expand Up @@ -372,12 +375,6 @@ func (v View) Validate() error {
if v.Proxy != ViewProxyNone && v.Proxy != ViewProxyEnv {
return invalidView("proxy %d", v.Proxy)
}
c := reflect.ValueOf(v.Capabilities)
for i := range c.NumField() {
if s := c.Field(i).Interface().(proto.CapabilitySupport); s != proto.CapabilitySupported && s != proto.CapabilityUnsupported {
return invalidView("capability %s is not declared", c.Type().Field(i).Name)
}
}
names := map[string]bool{ViewShimName: true, ViewHomeName: true, ViewRunName: true}
for _, m := range v.Closure {
if !isPathComponent(m.Name) || names[m.Name] {
Expand Down
1 change: 1 addition & 0 deletions apps/daemon/internal/agent/mcode/declaration.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ func discoverWithCheck(parent context.Context, options agent.DiscoveryOptions, r
// Native preparation verifies the applied admission/tool profile before input.
result.Capabilities.SubagentObservations = proto.CapabilitySupported
result.Capabilities.EnvironmentNone = proto.CapabilitySupported
result.Capabilities.LocalEnvironment = proto.CapabilitySupported
result.Capabilities.MCPHTTPTools = proto.CapabilitySupported
result.Capabilities.MCPHTTPBearerAuth = proto.CapabilitySupported
runtime.Info = result
Expand Down
4 changes: 2 additions & 2 deletions apps/daemon/internal/agent/mcode/declaration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ func TestMCodeExecutionFollowsAvailability(t *testing.T) {
if (runtime.Executor != nil) != available {
t.Fatalf("factories: %+v", runtime)
}
if info.Available != available || info.Capabilities.EnvironmentNone.IsSupported() != available || info.Capabilities.SubagentObservations.IsSupported() != available {
if info.Available != available || info.Capabilities.EnvironmentNone.IsSupported() != available || info.Capabilities.LocalEnvironment.IsSupported() != available || info.Capabilities.SubagentObservations.IsSupported() != available {
t.Fatalf("capabilities=%+v", info.Capabilities)
}
if info.Capabilities.NativeSessionRecovery.IsSupported() || info.Capabilities.LocalEnvironment.IsSupported() || info.Capabilities.FunctionTools.IsSupported() {
if info.Capabilities.NativeSessionRecovery.IsSupported() || info.Capabilities.FunctionTools.IsSupported() {
t.Fatal("unqualified capability advertised")
}
})
Expand Down
5 changes: 0 additions & 5 deletions apps/daemon/internal/agent/mcode/discovery_workspace.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (
"github.com/MiniMax-AI/OpenAgentCore/apps/daemon/internal/agent/binpath"
"github.com/MiniMax-AI/OpenAgentCore/apps/daemon/internal/localworkspace"
"github.com/MiniMax-AI/OpenAgentCore/apps/daemon/internal/paths"
"github.com/MiniMax-AI/OpenAgentCore/internal/agentdaemon/proto"
)

func discoverWorkspace(parent context.Context, options agent.DiscoveryOptions, runtime *agent.Runtime) *WorkspaceConfig {
Expand Down Expand Up @@ -45,10 +44,6 @@ func discoverWorkspace(parent context.Context, options agent.DiscoveryOptions, r
fail(err)
return nil
}

caps := &runtime.Info.Capabilities
caps.EnvironmentNone = proto.CapabilityUnsupported
caps.LocalEnvironment = proto.CapabilitySupported
return &c
}

Expand Down
10 changes: 1 addition & 9 deletions apps/daemon/internal/agent/mcode/view.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,15 +130,7 @@ func (i viewInstall) view() agent.View {
Shims: []string{"git", "rg"},
ShimPaths: []string{"/bin/bash"},
Proxy: agent.ViewProxyNone,
Capabilities: agent.ViewCapabilities{
EnvironmentNone: proto.CapabilitySupported,
Skills: proto.CapabilityUnsupported,
FunctionTools: proto.CapabilityUnsupported,
FunctionResultImages: proto.CapabilityUnsupported,
ToolSearch: proto.CapabilityUnsupported,
StdioMCP: proto.CapabilitySupported,
},
Executor: i.executor,
Executor: i.executor,
}
i.loader.AddTo(&view)
return view
Expand Down
2 changes: 1 addition & 1 deletion apps/daemon/internal/agent/mcode/view_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func viewFixture(t *testing.T) (viewInstall, agent.View, proto.PromptRequestPayl
registry := agent.NewRegistry()
info := Declaration.Info
info.Available = true
registry.Register(Declaration, agent.Runtime{Info: info, View: &declared})
registry.Register(Declaration, agent.Runtime{Info: info, View: &declared}, agent.EnvironmentSupport{Local: true, None: true})
view, err := registry.ResolveView("mcode")
if err != nil {
t.Fatal(err)
Expand Down
11 changes: 10 additions & 1 deletion apps/daemon/internal/agent/mcp_binding.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"strings"

"github.com/MiniMax-AI/OpenAgentCore/internal/agentdaemon/proto"
"github.com/MiniMax-AI/OpenAgentCore/internal/agentplugin"
)

// MCPBinding is the Runtime's transient effective declaration. Adapters project
Expand All @@ -27,6 +28,14 @@ type MCPBinding struct {
Stdio *proto.EnvironmentMCP
}

// EnvironmentMCPCredentials reports whether an installed MCP server takes
// credentials from the Environment's configuration: a bearer token variable,
// literal headers or environment variables. Its binding's credential
// authority is environment_configuration.
func EnvironmentMCPCredentials(server agentplugin.MCPServer) bool {
return server.BearerTokenEnvVar != "" || len(server.HTTPHeaders) > 0 || len(server.EnvVars) > 0
}

// ResolveMCPBindings combines public declarations with the frozen installation.
// Public declarations retain explicit origin and Vault authority; installed MCP
// retains Environment configuration authority. Neither may relocate implicitly.
Expand Down Expand Up @@ -59,7 +68,7 @@ func ResolveMCPBindings(req proto.PromptRequestPayload) ([]MCPBinding, error) {
if declaration.BearerTokenEnvVar != "" && installed.BearerToken == nil {
return nil, errors.New("environment MCP credential unavailable")
}
if installed.BearerToken != nil || len(declaration.HTTPHeaders) > 0 || len(declaration.EnvVars) > 0 {
if installed.BearerToken != nil || EnvironmentMCPCredentials(declaration) {
item.CredentialAuthority = "environment_configuration"
}
if declaration.Type == "stdio" {
Expand Down
Loading
Loading