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 apps/daemon/internal/agent/claudesdk/view.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ func prepareView(layout viewLayout, req proto.PromptRequestPayload, view agent.V
return start, env, nil
}
profile.NetworkAccess, profile.MCP = environment.NetworkAccess, servers
profile.Skills, profile.CapabilityRoot = environment.Skills, environment.CapabilityRoot
start.Workspace, start.Cwd = profile, environment.WorkspaceRoot
return start, env, nil
}
Expand Down
16 changes: 12 additions & 4 deletions apps/daemon/internal/agent/claudesdk/view_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@ import (
"github.com/MiniMax-AI/OpenAgentCore/apps/daemon/internal/agent"
"github.com/MiniMax-AI/OpenAgentCore/apps/daemon/internal/agent/clirunner"
"github.com/MiniMax-AI/OpenAgentCore/apps/daemon/internal/agent/viewloader"
"github.com/MiniMax-AI/OpenAgentCore/internal/agentcapabilities"
"github.com/MiniMax-AI/OpenAgentCore/internal/agentdaemon/proto"
"github.com/MiniMax-AI/OpenAgentCore/internal/agentplugin"
"github.com/MiniMax-AI/OpenAgentCore/internal/agentskill"
"github.com/MiniMax-AI/OpenAgentCore/internal/modelprovider"
)

Expand Down Expand Up @@ -105,19 +107,25 @@ func TestViewExecutorLaunchesAClosedGatewayEnvironment(t *testing.T) {
t.Fatal("the real key reached the view")
}

// The Harness runs a stdio binding's alias without arguments.
// The Harness gets the installed Skill in the sandbox's capability root
// and runs a stdio binding's alias without arguments.
docs := session.MCP
session.MCP = []agent.MCPBinding{{ServerLabel: "local", Transport: "stdio", Stdio: &proto.EnvironmentMCP{
Server: agentplugin.MCPServer{Name: "local", Type: "stdio", Command: agent.ViewAlias(0)}}}}
stdio, err := view.Executor(t.Context(), req, session)
installed, local := req, *req.LocalEnvironment
local.CapabilityRoot, local.Skills = agentcapabilities.Directory, []agentcapabilities.InstalledSkill{{InstallationRoot: agentcapabilities.Directory,
Metadata: agentskill.Metadata{Type: "inline", Name: "review", Description: "Review."}, RelativeRoot: "skills/review", PackageRoot: "skills/review"}}
installed.LocalEnvironment = &local
stdio, err := view.Executor(t.Context(), installed, session)
if err != nil {
t.Fatal(err)
}
defer stdio.Close(ctx)
var stdioStart startRequest
if err := json.Unmarshal(<-requests, &stdioStart); err != nil || stdioStart.Workspace == nil || len(stdioStart.Workspace.MCP) != 1 ||
stdioStart.Workspace.MCP[0].Command != agent.ViewAlias(0) || stdioStart.Workspace.MCP[0].Args != nil {
t.Fatalf("stdio MCP = %+v, %v", stdioStart.Workspace, err)
stdioStart.Workspace.MCP[0].Command != agent.ViewAlias(0) || stdioStart.Workspace.MCP[0].Args != nil ||
stdioStart.Workspace.CapabilityRoot != agentcapabilities.Directory || len(stdioStart.Workspace.Skills) != 1 || stdioStart.Workspace.Skills[0].RelativeRoot != "skills/review" {
t.Fatalf("installed Skill and stdio MCP = %+v, %v", stdioStart.Workspace, err)
}

// With environment none the bridge runs without a workspace in the work directory.
Expand Down
41 changes: 0 additions & 41 deletions apps/daemon/internal/agent/codex/hosted_skills.go

This file was deleted.

36 changes: 0 additions & 36 deletions apps/daemon/internal/agent/codex/hosted_skills_test.go

This file was deleted.

6 changes: 3 additions & 3 deletions apps/daemon/internal/agent/codex/mcp_http_bearer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ func TestMCPHTTPBearerPlanSeparatesServersAndProcesses(t *testing.T) {
req := proto.PromptRequestPayload{Model: "fixture", ModelProvider: fixtureProvider(), AgentStateKey: "retained-mcp", DisableExecutionEnvironment: true, MCPHTTPServers: &servers}
seen := map[string]bool{}
for range 2 {
plan, _, err := prepareSessionPlan(t.Context(), req, defaultSessionConfig())
plan, err := prepareSessionPlan(t.Context(), req, defaultSessionConfig())
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -58,7 +58,7 @@ func TestMCPHTTPBearerRejectsInvalidTokensWithoutPersistence(t *testing.T) {
for _, token := range []string{"", "=", " has-space", "has-space ", "has space", "line\r\ninjection", "nul\x00byte", "opaque中文", "middle=padding", "punctuation:invalid"} {
servers := []proto.MCPHTTPServer{{ConnectionOrigin: "service", ServerLabel: "tools", ServerURL: "https://tools.example/mcp", BearerToken: &token}}
req := proto.PromptRequestPayload{Model: "fixture", ModelProvider: fixtureProvider(), AgentStateKey: "invalid-bearer", DisableExecutionEnvironment: true, MCPHTTPServers: &servers}
if _, _, err := prepareSessionPlan(t.Context(), req, defaultSessionConfig()); err == nil || err.Error() != "invalid HTTPS MCP bearer credential" {
if _, err := prepareSessionPlan(t.Context(), req, defaultSessionConfig()); err == nil || err.Error() != "invalid HTTPS MCP bearer credential" {
t.Fatal("invalid bearer value accepted or unsafe error returned")
}
}
Expand All @@ -84,7 +84,7 @@ func TestMCPHTTPBearerDoesNotReachModelCatalogProbe(t *testing.T) {
Model: "fixture-model", ExecutionControls: &proto.ExecutionControls{WebSearch: "disabled", TextVerbosity: "medium"}}
cfg := defaultSessionConfig()
cfg.codexBinary = binary
plan, _, err := prepareSessionPlan(t.Context(), req, cfg)
plan, err := prepareSessionPlan(t.Context(), req, cfg)
if err != nil {
t.Fatal("catalog probe inherited bearer or failed", err)
}
Expand Down
6 changes: 3 additions & 3 deletions apps/daemon/internal/agent/codex/mcp_http_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ func TestPublicMCPHTTPPlanOwnsConfigurationAndPreservesHistory(t *testing.T) {
{ConnectionOrigin: "service", ServerLabel: "blocked", ServerURL: "http://127.0.0.1:12345/mcp", AllowedTools: &denyAll},
}
req := proto.PromptRequestPayload{Model: "fixture", ModelProvider: fixtureProvider(), AgentStateKey: "public-mcp", DisableExecutionEnvironment: true, MCPHTTPServers: &servers}
plan, _, err := prepareSessionPlan(t.Context(), req, defaultSessionConfig())
plan, err := prepareSessionPlan(t.Context(), req, defaultSessionConfig())
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -54,7 +54,7 @@ func TestPublicMCPHTTPPlanOwnsConfigurationAndPreservesHistory(t *testing.T) {
t.Fatal(err)
}
servers = []proto.MCPHTTPServer{{ConnectionOrigin: "service", ServerLabel: "replacement", ServerURL: "https://new.example/mcp"}}
second, _, err := prepareSessionPlan(t.Context(), req, defaultSessionConfig())
second, err := prepareSessionPlan(t.Context(), req, defaultSessionConfig())
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -100,7 +100,7 @@ func TestPublicMCPHTTPRejectsInvalidProfileAndStoredCredentials(t *testing.T) {
t.Fatal(err)
}
req := proto.PromptRequestPayload{Model: "fixture", ModelProvider: fixtureProvider(), AgentStateKey: "credentials", DisableExecutionEnvironment: true, MCPHTTPServers: &valid}
if _, _, err := prepareSessionPlan(t.Context(), req, defaultSessionConfig()); err == nil {
if _, err := prepareSessionPlan(t.Context(), req, defaultSessionConfig()); err == nil {
t.Fatal("existing MCP credentials accepted")
}
if after, err := os.ReadFile(path); err != nil || !reflect.DeepEqual(after, stored) {
Expand Down
6 changes: 3 additions & 3 deletions apps/daemon/internal/agent/codex/permission_profile_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ func TestRuntimeUsesHostPermissions(t *testing.T) {
t.Setenv("OAC_RUNTIME_HOME", t.TempDir())
{
req := proto.PromptRequestPayload{Model: "fixture", ModelProvider: fixtureProvider(), AgentStateKey: "session", DisableSubagents: true, LocalEnvironment: &proto.LocalEnvironment{NetworkAccess: "enabled", WorkspaceRoot: t.TempDir()}}
plan, _, err := prepareSessionPlan(t.Context(), req, sessionConfig{})
plan, err := prepareSessionPlan(t.Context(), req, sessionConfig{})
if err != nil {
t.Fatal(err)
}
Expand All @@ -27,7 +27,7 @@ func TestRuntimeUsesHostPermissions(t *testing.T) {
plan.Cleanup()
for _, network := range []string{"disabled", "restricted"} {
req.LocalEnvironment.NetworkAccess = network
if _, _, err := prepareSessionPlan(t.Context(), req, sessionConfig{}); err == nil {
if _, err := prepareSessionPlan(t.Context(), req, sessionConfig{}); err == nil {
t.Fatal("unsupported network admitted")
}
}
Expand All @@ -49,7 +49,7 @@ func TestSelfHostedToolEnvironmentCannotRedirectNativeHistory(t *testing.T) {
t.Setenv(key, value)
}
req := proto.PromptRequestPayload{Model: "fixture", ModelProvider: fixtureProvider(), AgentStateKey: "session", DisableSubagents: true, LocalEnvironment: &proto.LocalEnvironment{NetworkAccess: "enabled"}}
plan, _, err := prepareSessionPlan(t.Context(), req, sessionConfig{})
plan, err := prepareSessionPlan(t.Context(), req, sessionConfig{})
if err != nil {
t.Fatal(err)
}
Expand Down
9 changes: 4 additions & 5 deletions apps/daemon/internal/agent/codex/preparation.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,10 @@ func newExecutor(parent context.Context, req proto.PromptRequestPayload, cfg ses
return nil, err
}
var plan SessionPlan
var skillRoots []string
if cfg.view != nil {
plan, err = prepareViewPlan(parent, req, cfg)
} else {
plan, skillRoots, err = prepareSessionPlan(parent, req, cfg)
plan, err = prepareSessionPlan(parent, req, cfg)
}
if err != nil {
return nil, err
Expand Down Expand Up @@ -106,9 +105,9 @@ func newExecutor(parent context.Context, req proto.PromptRequestPayload, cfg ses
return e.preparationFailed(err)
}
}
if len(skillRoots) > 0 {
if err := setSkillExtraRoots(cancelCtx, rpc, skillRoots); err != nil {
return e.preparationFailed(fmt.Errorf("codex: register skill root: %w", err))
if local := req.LocalEnvironment; local != nil && len(local.Skills) > 0 {
if err := registerSkills(cancelCtx, rpc, plan.Cwd, local.Skills); err != nil {
return e.preparationFailed(err)
}
}

Expand Down
12 changes: 12 additions & 0 deletions apps/daemon/internal/agent/codex/preparation_helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ func TestPreparationFakeCodexProcess(t *testing.T) {
turnNumber := 0
currentTurn := ""
executorMode := os.Getenv("OAC_TEST_EXECUTOR_MODE")
var skills SkillsExtraRootsSetParams
for scanner.Scan() {
var frame preparationFrame
if json.Unmarshal(scanner.Bytes(), &frame) != nil {
Expand All @@ -172,6 +173,17 @@ func TestPreparationFakeCodexProcess(t *testing.T) {
status = string(data)
}
result = map[string]string{"status": status}
case "skills/extraRoots/set":
if json.Unmarshal(frame.Params, &skills) != nil {
os.Exit(7)
}
case "skills/list":
// Each registered root holds one Skill.
listed := []map[string]string{}
for _, root := range skills.ExtraRoots {
listed = append(listed, map[string]string{"name": filepath.Base(root), "path": filepath.Join(root, "SKILL.md")})
}
result = map[string]any{"data": []any{map[string]any{"skills": listed, "errors": []any{}}}}
case "config/read":
data, err := os.ReadFile(os.Getenv("OAC_TEST_PREPARATION_MCP_CONFIG"))
if err != nil || json.Unmarshal(data, &result) != nil {
Expand Down
18 changes: 18 additions & 0 deletions apps/daemon/internal/agent/codex/protocol.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,24 @@ type SkillsExtraRootsSetParams struct {
ExtraRoots []string `json:"extraRoots"`
}

type SkillsListParams struct {
Cwds []string `json:"cwds"`
ForceReload bool `json:"forceReload"`
}

// SkillsListResponse holds the fields of skills/list that the adapter checks.
type SkillsListResponse struct {
Data []struct {
Skills []struct {
Path string `json:"path"`
Dependencies any `json:"dependencies"`
} `json:"skills"`
Errors []struct {
Path string `json:"path"`
} `json:"errors"`
} `json:"data"`
}

// ---------------------------------------------------------------------------
// thread/start, thread/resume, thread/list
// ---------------------------------------------------------------------------
Expand Down
31 changes: 10 additions & 21 deletions apps/daemon/internal/agent/codex/session_plan.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,24 @@ import (
"github.com/MiniMax-AI/OpenAgentCore/internal/agentdaemon/proto"
)

func prepareSessionPlan(ctx context.Context, req proto.PromptRequestPayload, cfg sessionConfig) (SessionPlan, []string, error) {
func prepareSessionPlan(ctx context.Context, req proto.PromptRequestPayload, cfg sessionConfig) (SessionPlan, error) {
if err := validateNativeTransportEnvironment(); err != nil {
return SessionPlan{}, nil, err
return SessionPlan{}, err
}
if err := validatePermissionProfile(req); err != nil {
return SessionPlan{}, nil, err
return SessionPlan{}, err
}
mcpServers, mcpEnv, err := runtimeMCPServers(req)
if err != nil {
return SessionPlan{}, nil, err
return SessionPlan{}, err
}
plan, err := BuildSessionPlan(req)
if err != nil {
return SessionPlan{}, nil, fmt.Errorf("codex: build session plan: %w", err)
return SessionPlan{}, fmt.Errorf("codex: build session plan: %w", err)
}
if err := configureSubagentObservations(&plan, req); err != nil {
plan.Cleanup()
return SessionPlan{}, nil, err
return SessionPlan{}, err
}
disableProgrammaticTools(&plan, req.ExecutionControls)
if req.LocalEnvironment != nil {
Expand All @@ -40,7 +40,7 @@ func prepareSessionPlan(ctx context.Context, req proto.PromptRequestPayload, cfg
values, err := localworkspace.ReadOptionalToolEnvironment()
if err != nil {
plan.Cleanup()
return SessionPlan{}, nil, err
return SessionPlan{}, err
}
for key, value := range values {
if strings.EqualFold(key, "CODEX_HOME") || strings.EqualFold(key, "HOME") || strings.EqualFold(key, "USERPROFILE") {
Expand All @@ -56,29 +56,18 @@ func prepareSessionPlan(ctx context.Context, req proto.PromptRequestPayload, cfg
if mcpServers != nil {
if err := configureMCP(&plan, mcpServers); err != nil {
plan.Cleanup()
return SessionPlan{}, nil, err
return SessionPlan{}, err
}
}

if req.ExecutionControls != nil {
if err := prepareModelVerbosity(ctx, cfg.codexBinary, &plan); err != nil {
plan.Cleanup()
return SessionPlan{}, nil, err
}
}

var skillRoots []string
if req.LocalEnvironment != nil && len(req.LocalEnvironment.Skills) > 0 {
if err := verifyHostedSkills(req.LocalEnvironment.Skills); err != nil {
plan.Cleanup()
return SessionPlan{}, nil, err
}
for _, skill := range req.LocalEnvironment.Skills {
skillRoots = append(skillRoots, localworkspace.SkillPath(skill))
return SessionPlan{}, err
}
}

plan.Env = append(plan.Env, mcpEnv...)

return plan, skillRoots, nil
return plan, nil
}
2 changes: 1 addition & 1 deletion apps/daemon/internal/agent/codex/session_policy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ func TestThreadRequestsApplyDeploymentPolicy(t *testing.T) {
for _, method := range []string{"thread/start", "thread/resume"} {
t.Run(method, func(t *testing.T) {
t.Setenv("OAC_RUNTIME_HOME", t.TempDir())
plan, _, err := prepareSessionPlan(context.Background(), proto.PromptRequestPayload{Model: "fixture", ModelProvider: fixtureProvider(), AgentStateKey: "conv/agent/codex", DisableSubagents: true, DisableExecutionEnvironment: true}, sessionConfig{})
plan, err := prepareSessionPlan(context.Background(), proto.PromptRequestPayload{Model: "fixture", ModelProvider: fixtureProvider(), AgentStateKey: "conv/agent/codex", DisableSubagents: true, DisableExecutionEnvironment: true}, sessionConfig{})
if err != nil {
t.Fatal(err)
}
Expand Down
Loading
Loading