Skip to content
Closed
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
39 changes: 38 additions & 1 deletion .github/workflows/dev.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions .github/workflows/dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ engine:
max-turns: 5
tools:
cache-memory: true
playwright: null
permissions: read-all
concurrency:
group: "gh-aw-${{ github.workflow }}-${{ github.ref }}"
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

36 changes: 36 additions & 0 deletions pkg/workflow/agentic_engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,42 @@ func (r *EngineRegistry) GetAllEngines() []CodingAgentEngine {
return engines
}

// GetCopilotAgentPlaywrightTools returns the list of playwright tools available in the copilot agent
// This matches the tools available in the copilot agent MCP server configuration
// This is a shared function used by all engines for consistent playwright tool configuration
func GetCopilotAgentPlaywrightTools() []any {
tools := []string{
"browser_click",
"browser_close",
"browser_console_messages",
"browser_drag",
"browser_evaluate",
"browser_file_upload",
"browser_fill_form",
"browser_handle_dialog",
"browser_hover",
"browser_install",
"browser_navigate",
"browser_navigate_back",
"browser_network_requests",
"browser_press_key",
"browser_resize",
"browser_select_option",
"browser_snapshot",
"browser_tabs",
"browser_take_screenshot",
"browser_type",
"browser_wait_for",
}

// Convert []string to []any for compatibility with the configuration system
result := make([]any, len(tools))
for i, tool := range tools {
result[i] = tool
}
return result
}

// ConvertStepToYAML converts a step map to YAML string with proper indentation
// This is a shared utility function used by all engines and the compiler
func ConvertStepToYAML(stepMap map[string]any) (string, error) {
Expand Down
13 changes: 11 additions & 2 deletions pkg/workflow/claude_engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,15 @@ func (e *ClaudeEngine) expandNeutralToolsToClaudeTools(tools map[string]any) map
_ = editTool
}

// Handle playwright tool by converting it to an MCP tool configuration
if _, hasPlaywright := tools["playwright"]; hasPlaywright {
// Create playwright as an MCP tool with the same tools available as copilot agent
playwrightMCP := map[string]any{
"allowed": GetCopilotAgentPlaywrightTools(),
}
result["playwright"] = playwrightMCP
}

// Update claude section
claudeSection["allowed"] = claudeAllowed
result["claude"] = claudeSection
Expand Down Expand Up @@ -468,8 +477,8 @@ func (e *ClaudeEngine) computeAllowedClaudeToolsString(tools map[string]any, saf
isCustomMCP = true
}

// Handle standard MCP tools (github) or tools with MCP-compatible type
if toolName == "github" || isCustomMCP {
// Handle standard MCP tools (github, playwright) or tools with MCP-compatible type
if toolName == "github" || toolName == "playwright" || isCustomMCP {
if allowed, hasAllowed := mcpConfig["allowed"]; hasAllowed {
if allowedSlice, ok := allowed.([]any); ok {
// Check for wildcard access first
Expand Down
7 changes: 7 additions & 0 deletions pkg/workflow/claude_engine_tools_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,13 @@ func TestClaudeEngineComputeAllowedTools(t *testing.T) {
},
expected: "Bash,BashOutput,ExitPlanMode,Glob,Grep,KillBash,LS,NotebookRead,Read,Task,TodoWrite",
},
{
name: "neutral playwright tool",
tools: map[string]any{
"playwright": nil,
},
expected: "ExitPlanMode,Glob,Grep,LS,NotebookRead,Read,Task,TodoWrite,mcp__playwright__browser_click,mcp__playwright__browser_close,mcp__playwright__browser_console_messages,mcp__playwright__browser_drag,mcp__playwright__browser_evaluate,mcp__playwright__browser_file_upload,mcp__playwright__browser_fill_form,mcp__playwright__browser_handle_dialog,mcp__playwright__browser_hover,mcp__playwright__browser_install,mcp__playwright__browser_navigate,mcp__playwright__browser_navigate_back,mcp__playwright__browser_network_requests,mcp__playwright__browser_press_key,mcp__playwright__browser_resize,mcp__playwright__browser_select_option,mcp__playwright__browser_snapshot,mcp__playwright__browser_tabs,mcp__playwright__browser_take_screenshot,mcp__playwright__browser_type,mcp__playwright__browser_wait_for",
},
}

for _, tt := range tests {
Expand Down
40 changes: 37 additions & 3 deletions pkg/workflow/codex_engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,25 +169,59 @@ func (e *CodexEngine) convertStepToYAML(stepMap map[string]any) (string, error)
return ConvertStepToYAML(stepMap)
}

// expandNeutralToolsToCodexTools converts neutral tools to Codex-specific tools format
// This ensures that playwright tools get the same allowlist as the copilot agent
func (e *CodexEngine) expandNeutralToolsToCodexTools(tools map[string]any) map[string]any {
result := make(map[string]any)

// Copy all existing tools
for key, value := range tools {
result[key] = value
}

// Handle playwright tool by converting it to an MCP tool configuration with copilot agent tools
if _, hasPlaywright := tools["playwright"]; hasPlaywright {
// Create playwright as an MCP tool with the same tools available as copilot agent
playwrightMCP := map[string]any{
"allowed": GetCopilotAgentPlaywrightTools(),
}
// If the original playwright tool has additional configuration (like docker_image_version),
// preserve it while adding the allowed tools
if playwrightConfig, ok := tools["playwright"].(map[string]any); ok {
for key, value := range playwrightConfig {
playwrightMCP[key] = value
}
}
// Always set the allowed tools to match copilot agent
playwrightMCP["allowed"] = GetCopilotAgentPlaywrightTools()
result["playwright"] = playwrightMCP
}

return result
}

func (e *CodexEngine) RenderMCPConfig(yaml *strings.Builder, tools map[string]any, mcpTools []string, workflowData *WorkflowData) {
yaml.WriteString(" cat > /tmp/mcp-config/config.toml << EOF\n")

// Add history configuration to disable persistence
yaml.WriteString(" [history]\n")
yaml.WriteString(" persistence = \"none\"\n")

// Expand neutral tools (like playwright: null) to include the copilot agent tools
expandedTools := e.expandNeutralToolsToCodexTools(tools)

// Generate [mcp_servers] section
for _, toolName := range mcpTools {
switch toolName {
case "github":
githubTool := tools["github"]
githubTool := expandedTools["github"]
e.renderGitHubCodexMCPConfig(yaml, githubTool, workflowData)
case "playwright":
playwrightTool := tools["playwright"]
playwrightTool := expandedTools["playwright"]
e.renderPlaywrightCodexMCPConfig(yaml, playwrightTool, workflowData.NetworkPermissions)
default:
// Handle custom MCP tools (those with MCP-compatible type)
if toolConfig, ok := tools[toolName].(map[string]any); ok {
if toolConfig, ok := expandedTools[toolName].(map[string]any); ok {
if hasMcp, _ := hasMCPConfig(toolConfig); hasMcp {
if err := e.renderCodexMCPConfig(yaml, toolName, toolConfig); err != nil {
fmt.Printf("Error generating custom MCP configuration for %s: %v\n", toolName, err)
Expand Down
Loading