From 444992ec7a903e5a88618c07b3c3c99001cee44b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 26 Jun 2026 18:31:10 +0000 Subject: [PATCH 01/13] Add Copilot-only LSP frontmatter support Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/parser/schema_test.go | 23 ++++ pkg/parser/schemas/main_workflow_schema.json | 35 +++++ .../compiler_orchestrator_workflow.go | 1 + .../compiler_orchestrator_workflow_test.go | 20 +++ pkg/workflow/compiler_types.go | 3 +- pkg/workflow/copilot_engine_execution.go | 42 ++++-- pkg/workflow/copilot_engine_installation.go | 21 ++- pkg/workflow/copilot_engine_test.go | 60 +++++++- pkg/workflow/copilot_home_expansion_test.go | 4 +- pkg/workflow/frontmatter_types.go | 17 +-- pkg/workflow/lsp_manager.go | 130 ++++++++++++++++++ pkg/workflow/lsp_manager_test.go | 54 ++++++++ pkg/workflow/lsp_validation.go | 28 ++++ pkg/workflow/lsp_validation_test.go | 57 ++++++++ pkg/workflow/workflow_builder.go | 27 ++++ 15 files changed, 495 insertions(+), 27 deletions(-) create mode 100644 pkg/workflow/lsp_manager.go create mode 100644 pkg/workflow/lsp_manager_test.go create mode 100644 pkg/workflow/lsp_validation.go create mode 100644 pkg/workflow/lsp_validation_test.go diff --git a/pkg/parser/schema_test.go b/pkg/parser/schema_test.go index 97d05cbf35b..ef88f72c1f5 100644 --- a/pkg/parser/schema_test.go +++ b/pkg/parser/schema_test.go @@ -491,6 +491,29 @@ func TestValidateMainWorkflowFrontmatterWithSchemaAndLocation_ToolsEditBoolean(t } } +func TestValidateMainWorkflowFrontmatterWithSchemaAndLocation_LSPConfig(t *testing.T) { + t.Parallel() + + frontmatter := map[string]any{ + "on": "push", + "engine": "copilot", + "lsp": map[string]any{ + "typescript": map[string]any{ + "command": "typescript-language-server", + "args": []any{"--stdio"}, + "fileExtensions": map[string]any{ + ".ts": "typescript", + }, + }, + }, + } + + err := ValidateMainWorkflowFrontmatterWithSchemaAndLocation(frontmatter, "/tmp/gh-aw/lsp-config-test.md") + if err != nil { + t.Fatalf("expected valid lsp configuration to pass schema validation, got: %v", err) + } +} + func TestValidateMainWorkflowFrontmatterWithSchemaAndLocation_MaxLimitsAllowExpressions(t *testing.T) { t.Parallel() diff --git a/pkg/parser/schemas/main_workflow_schema.json b/pkg/parser/schemas/main_workflow_schema.json index c38d2782a33..2c95e869794 100644 --- a/pkg/parser/schemas/main_workflow_schema.json +++ b/pkg/parser/schemas/main_workflow_schema.json @@ -4710,6 +4710,41 @@ }, "additionalProperties": false }, + "lsp": { + "type": "object", + "description": "Top-level Language Server Protocol (LSP) configuration for Copilot CLI. Each key is a language identifier and each value defines the server command, args, and file extension mappings.", + "patternProperties": { + "^[a-zA-Z0-9_-]+$": { + "type": "object", + "properties": { + "command": { + "type": "string", + "minLength": 1, + "description": "Executable command for the language server" + }, + "args": { + "type": "array", + "description": "Optional command-line arguments passed to the language server executable", + "items": { + "type": "string" + } + }, + "fileExtensions": { + "type": "object", + "description": "Map of file extension to language id, for example {\".ts\": \"typescript\"}", + "minProperties": 1, + "additionalProperties": { + "type": "string", + "minLength": 1 + } + } + }, + "required": ["command", "fileExtensions"], + "additionalProperties": false + } + }, + "additionalProperties": false + }, "cache": { "description": "Cache configuration for workflow (uses actions/cache syntax)", "oneOf": [ diff --git a/pkg/workflow/compiler_orchestrator_workflow.go b/pkg/workflow/compiler_orchestrator_workflow.go index 506e89b6c6c..e4f3c21d3ef 100644 --- a/pkg/workflow/compiler_orchestrator_workflow.go +++ b/pkg/workflow/compiler_orchestrator_workflow.go @@ -145,6 +145,7 @@ func (c *Compiler) validateWorkflowEngineSettings(cleanPath string, workflowData c.validateRunInstallScripts, c.validateEngineVersion, c.validatePlaywrightMode, + c.validateLSPSupport, c.validateEngineHarnessScript, c.validateEngineDriver, c.validateEngineMCPSessionTimeout, diff --git a/pkg/workflow/compiler_orchestrator_workflow_test.go b/pkg/workflow/compiler_orchestrator_workflow_test.go index dc241190a9d..b74b0f27a0e 100644 --- a/pkg/workflow/compiler_orchestrator_workflow_test.go +++ b/pkg/workflow/compiler_orchestrator_workflow_test.go @@ -321,6 +321,26 @@ func TestValidateWorkflowEngineSettings_PreservesLegacyErrorOrder(t *testing.T) assert.NotContains(t, err.Error(), "engine.harness") } +func TestValidateWorkflowEngineSettings_LSPRequiresCopilot(t *testing.T) { + compiler := NewCompiler() + workflowData := &WorkflowData{ + AI: "codex", + LSP: map[string]LSPServerConfig{ + "go": { + Command: "gopls", + Args: []string{"serve"}, + FileExtensions: map[string]string{ + ".go": "go", + }, + }, + }, + } + + err := compiler.validateWorkflowEngineSettings("workflow.md", workflowData) + require.Error(t, err) + assert.Contains(t, err.Error(), "workflow.md: lsp is currently only supported for engine: copilot") +} + func TestMergeRawOTLPEndpoints_DedupesAndCountsSources(t *testing.T) { mainObs := map[string]any{ "otlp": map[string]any{ diff --git a/pkg/workflow/compiler_types.go b/pkg/workflow/compiler_types.go index ef9faed82fb..c03ee0f7dc5 100644 --- a/pkg/workflow/compiler_types.go +++ b/pkg/workflow/compiler_types.go @@ -505,7 +505,8 @@ type WorkflowData struct { Container string // container setting for the main job Services string // services setting for the main job Tools map[string]any - ParsedTools *Tools // Structured tools configuration (NEW: parsed from Tools map) + LSP map[string]LSPServerConfig // top-level LSP server configuration for Copilot CLI + ParsedTools *Tools // Structured tools configuration (NEW: parsed from Tools map) MarkdownContent string AI string // "claude" or "codex" (for backwards compatibility) EngineConfig *EngineConfig // Extended engine configuration diff --git a/pkg/workflow/copilot_engine_execution.go b/pkg/workflow/copilot_engine_execution.go index c3a1106fd09..09a0637b628 100644 --- a/pkg/workflow/copilot_engine_execution.go +++ b/pkg/workflow/copilot_engine_execution.go @@ -47,22 +47,42 @@ const customEngineCommandScriptPath = "/tmp/gh-aw/engine-command.sh" // other generators (copilot_mcp.go, mcp_setup_generator.go) rely on it the same way. const copilotSettingsPath = "$HOME/.copilot/settings.json" -// copilotSettingsContent is the JSON content written to the Copilot CLI settings file. -// Setting builtInAgents.rubberDuck to false disables the rubber-duck sub-agent, which -// would otherwise be invoked proactively for non-trivial tasks. In Copilot engine runs -// this adds unnecessary token overhead and latency with little benefit, since the primary -// model already has strong reasoning capabilities. -const copilotSettingsContent = `{"builtInAgents":{"rubberDuck":false}}` +// copilotSettingsDefaultContent is the default JSON content written to the Copilot CLI +// settings file when no additional settings are configured. +const copilotSettingsDefaultContent = `{"builtInAgents":{"rubberDuck":false}}` + +type copilotSettings struct { + BuiltInAgents map[string]bool `json:"builtInAgents"` + LSPServers map[string]LSPServerConfig `json:"lspServers,omitempty"` +} + +func buildCopilotSettingsContent(workflowData *WorkflowData) string { + settings := copilotSettings{ + BuiltInAgents: map[string]bool{"rubberDuck": false}, + } + if workflowData != nil { + manager := NewLSPManager(workflowData.LSP) + settings.LSPServers = manager.CopilotLSPServers() + } + settingsBytes, err := json.Marshal(settings) + if err != nil { + return copilotSettingsDefaultContent + } + return string(settingsBytes) +} // buildCopilotSettingsSetup returns shell commands that write the Copilot CLI settings // file before the agent runs, disabling the rubber-duck sub-agent. -func buildCopilotSettingsSetup(fixOwnershipForCustomCommand bool) string { +func buildCopilotSettingsSetup(settingsContent string, fixOwnershipForCustomCommand bool) string { + if settingsContent == "" { + settingsContent = copilotSettingsDefaultContent + } setup := "mkdir -p \"$HOME/.copilot\"\n" if fixOwnershipForCustomCommand { setup += "sudo chown -R \"$(id -u):$(id -g)\" \"$HOME/.copilot\"\n" } return setup + fmt.Sprintf("printf '%%s' %s > \"%s\"\n", - shellEscapeArg(copilotSettingsContent), copilotSettingsPath) + shellEscapeArg(settingsContent), copilotSettingsPath) } // buildCopilotSettingsCleanupTrap returns a shell trap command that removes the @@ -382,6 +402,8 @@ func (e *CopilotEngine) GetExecutionSteps(workflowData *WorkflowData, logFile st // Non-sandbox mode: pass prompt file path directly copilotCommand = fmt.Sprintf(`%s %s --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt`, execPrefix, shellJoinArgs(copilotArgs)) } + settingsContent := buildCopilotSettingsContent(workflowData) + // Conditionally wrap with sandbox (AWF only) var command string if isFirewallEnabled(workflowData) { @@ -451,7 +473,7 @@ func (e *CopilotEngine) GetExecutionSteps(workflowData *WorkflowData, logFile st // Write the Copilot settings file before AWF starts. The file is created on the // host and AWF mounts it into the container, where the Copilot CLI reads it to // disable the rubber-duck sub-agent. - pathSetup = buildCopilotSettingsCleanupTrap() + buildCopilotSettingsSetup(customCommandScriptSetup != "") + buildCopilotMCPConfigExport(workflowData) + pathSetup + pathSetup = buildCopilotSettingsCleanupTrap() + buildCopilotSettingsSetup(settingsContent, customCommandScriptSetup != "") + buildCopilotMCPConfigExport(workflowData) + pathSetup // Build the list of core secret var names to hide from the agent shell tools. // In BYOK mode COPILOT_GITHUB_TOKEN is not injected into the step env at all, // so there is nothing to exclude. Excluding it unconditionally would produce @@ -495,7 +517,7 @@ func (e *CopilotEngine) GetExecutionSteps(workflowData *WorkflowData, logFile st } // Write the Copilot settings file before the agent runs to disable the rubber-duck // sub-agent. This reduces token overhead and latency for Copilot engine runs. - preCommandSetup = buildCopilotSettingsCleanupTrap() + buildCopilotSettingsSetup(customCommandScriptSetup != "") + buildCopilotMCPConfigExport(workflowData) + preCommandSetup + preCommandSetup = buildCopilotSettingsCleanupTrap() + buildCopilotSettingsSetup(settingsContent, customCommandScriptSetup != "") + buildCopilotMCPConfigExport(workflowData) + preCommandSetup command = fmt.Sprintf(`set -o pipefail printf '%%s' "$(date +%%s%%3N)" > %s touch %s diff --git a/pkg/workflow/copilot_engine_installation.go b/pkg/workflow/copilot_engine_installation.go index 89c79ff4e0d..dba60b88ce8 100644 --- a/pkg/workflow/copilot_engine_installation.go +++ b/pkg/workflow/copilot_engine_installation.go @@ -96,14 +96,14 @@ func (e *CopilotEngine) GetInstallationSteps(workflowData *WorkflowData) []GitHu if len(sdkInstallStep) > 0 { steps = append(steps, sdkInstallStep) } - return BuildNpmEngineInstallStepsWithAWF(steps, workflowData) + return appendCopilotLSPInstallSteps(BuildNpmEngineInstallStepsWithAWF(steps, workflowData), workflowData) } if len(sdkInstallStep) > 0 { copilotInstallLog.Printf("Skipping Copilot CLI installation: custom command specified (%s); keeping Copilot SDK install step", workflowData.EngineConfig.Command) - return []GitHubActionStep{sdkInstallStep} + return appendCopilotLSPInstallSteps([]GitHubActionStep{sdkInstallStep}, workflowData) } copilotInstallLog.Printf("Skipping installation steps: custom command specified (%s)", workflowData.EngineConfig.Command) - return []GitHubActionStep{} + return appendCopilotLSPInstallSteps([]GitHubActionStep{}, workflowData) } // Copilot CLI is pinned to the default version constant. @@ -131,7 +131,20 @@ func (e *CopilotEngine) GetInstallationSteps(workflowData *WorkflowData) []GitHu } steps := BuildNpmEngineInstallStepsWithAWF(npmSteps, workflowData) - return steps + return appendCopilotLSPInstallSteps(steps, workflowData) +} + +func appendCopilotLSPInstallSteps(steps []GitHubActionStep, workflowData *WorkflowData) []GitHubActionStep { + if workflowData == nil { + return steps + } + manager := NewLSPManager(workflowData.LSP) + lspSteps := manager.GenerateInstallSteps() + if len(lspSteps) == 0 { + return steps + } + copilotInstallLog.Printf("Adding %d LSP dependency installation step(s)", len(lspSteps)) + return append(steps, lspSteps...) } func buildCopilotSDKInstallStep(workflowData *WorkflowData) GitHubActionStep { diff --git a/pkg/workflow/copilot_engine_test.go b/pkg/workflow/copilot_engine_test.go index 1037dcecb1f..91c83609a17 100644 --- a/pkg/workflow/copilot_engine_test.go +++ b/pkg/workflow/copilot_engine_test.go @@ -248,8 +248,8 @@ func TestCopilotEngineDisablesRubberDuck(t *testing.T) { if !strings.Contains(stepContent, "mkdir -p \"$HOME/.copilot\"") { t.Errorf("Expected 'mkdir -p \"$HOME/.copilot\"' in step content:\n%s", stepContent) } - if !strings.Contains(stepContent, copilotSettingsContent) { - t.Errorf("Expected copilot settings content %q in step content:\n%s", copilotSettingsContent, stepContent) + if !strings.Contains(stepContent, copilotSettingsDefaultContent) { + t.Errorf("Expected copilot settings content %q in step content:\n%s", copilotSettingsDefaultContent, stepContent) } if !strings.Contains(stepContent, copilotSettingsPath) { t.Errorf("Expected copilot settings path %q in step content:\n%s", copilotSettingsPath, stepContent) @@ -259,6 +259,62 @@ func TestCopilotEngineDisablesRubberDuck(t *testing.T) { } } +func TestCopilotEngineExecutionSteps_WithLSPConfig(t *testing.T) { + engine := NewCopilotEngine() + workflowData := &WorkflowData{ + Name: "test-workflow", + LSP: map[string]LSPServerConfig{ + "typescript": { + Command: "typescript-language-server", + Args: []string{"--stdio"}, + FileExtensions: map[string]string{ + ".ts": "typescript", + }, + }, + }, + } + + steps := engine.GetExecutionSteps(workflowData, "/tmp/gh-aw/test.log") + if len(steps) != 1 { + t.Fatalf("Expected 1 execution step, got %d", len(steps)) + } + + stepContent := strings.Join([]string(steps[0]), "\n") + if !strings.Contains(stepContent, `"lspServers":{"typescript":{"command":"typescript-language-server","args":["--stdio"],"fileExtensions":{".ts":"typescript"}}}`) { + t.Fatalf("Expected lspServers config in step content, got:\n%s", stepContent) + } +} + +func TestCopilotEngineInstallationSteps_WithLSPConfig(t *testing.T) { + engine := NewCopilotEngine() + workflowData := &WorkflowData{ + Name: "test-workflow", + LSP: map[string]LSPServerConfig{ + "python": { + Command: "pyright-langserver", + Args: []string{"--stdio"}, + FileExtensions: map[string]string{ + ".py": "python", + }, + }, + }, + } + + steps := engine.GetInstallationSteps(workflowData) + var allLines strings.Builder + for _, step := range steps { + allLines.WriteString(strings.Join(step, "\n")) + allLines.WriteByte('\n') + } + allLinesStr := allLines.String() + if !strings.Contains(allLinesStr, "Install Python LSP dependencies") { + t.Fatalf("Expected Python LSP install step, got:\n%s", allLinesStr) + } + if !strings.Contains(allLinesStr, "npm install -g pyright") { + t.Fatalf("Expected pyright install command, got:\n%s", allLinesStr) + } +} + func TestCopilotEngineExecutionStepsWithOutput(t *testing.T) { engine := NewCopilotEngine() workflowData := &WorkflowData{ diff --git a/pkg/workflow/copilot_home_expansion_test.go b/pkg/workflow/copilot_home_expansion_test.go index 7a1c65b4f2b..df96f16b314 100644 --- a/pkg/workflow/copilot_home_expansion_test.go +++ b/pkg/workflow/copilot_home_expansion_test.go @@ -74,7 +74,7 @@ func TestBuildCopilotSettingsSetup_UsesHomeExpansion(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got := buildCopilotSettingsSetup(tt.fixOwnershipForCustom) + got := buildCopilotSettingsSetup(copilotSettingsDefaultContent, tt.fixOwnershipForCustom) // Must reference $HOME, never the literal /home/runner. assert.Contains(t, got, `mkdir -p "$HOME/.copilot"`, @@ -304,7 +304,7 @@ func TestBashIntegration_SettingsSetupAndCleanupTrap(t *testing.T) { // The setup helper unconditionally tries to run `sudo` if the chown flag // is true; the second variant covers the no-sudo path so the test does not // depend on sudoers being configured. - setup := buildCopilotSettingsSetup(false) + setup := buildCopilotSettingsSetup(copilotSettingsDefaultContent, false) trap := buildCopilotSettingsCleanupTrap() for _, hv := range homeValuesUnderTest { diff --git a/pkg/workflow/frontmatter_types.go b/pkg/workflow/frontmatter_types.go index 44fd4a160fe..975fa3d7fff 100644 --- a/pkg/workflow/frontmatter_types.go +++ b/pkg/workflow/frontmatter_types.go @@ -310,14 +310,15 @@ type FrontmatterConfig struct { Labels []string `json:"labels,omitempty"` // Configuration sections - using strongly-typed structs - Tools *ToolsConfig `json:"tools,omitempty"` - MCPServers map[string]any `json:"mcp-servers,omitempty"` // Legacy field, use Tools instead - RuntimesTyped *RuntimesConfig `json:"-"` // New typed field (not in JSON to avoid conflict) - Runtimes map[string]any `json:"runtimes,omitempty"` // Deprecated: use RuntimesTyped - Jobs map[string]any `json:"jobs,omitempty"` // Custom workflow jobs (too dynamic to type) - SafeOutputs *SafeOutputsConfig `json:"safe-outputs,omitempty"` - MCPScripts *MCPScriptsConfig `json:"mcp-scripts,omitempty"` - PermissionsTyped *PermissionsConfig `json:"-"` // New typed field (not in JSON to avoid conflict) + Tools *ToolsConfig `json:"tools,omitempty"` + LSP map[string]LSPServerConfig `json:"lsp,omitempty"` + MCPServers map[string]any `json:"mcp-servers,omitempty"` // Legacy field, use Tools instead + RuntimesTyped *RuntimesConfig `json:"-"` // New typed field (not in JSON to avoid conflict) + Runtimes map[string]any `json:"runtimes,omitempty"` // Deprecated: use RuntimesTyped + Jobs map[string]any `json:"jobs,omitempty"` // Custom workflow jobs (too dynamic to type) + SafeOutputs *SafeOutputsConfig `json:"safe-outputs,omitempty"` + MCPScripts *MCPScriptsConfig `json:"mcp-scripts,omitempty"` + PermissionsTyped *PermissionsConfig `json:"-"` // New typed field (not in JSON to avoid conflict) // Event and trigger configuration On map[string]any `json:"on,omitempty"` // Complex trigger config with many variants (too dynamic to type) diff --git a/pkg/workflow/lsp_manager.go b/pkg/workflow/lsp_manager.go new file mode 100644 index 00000000000..18ef355b923 --- /dev/null +++ b/pkg/workflow/lsp_manager.go @@ -0,0 +1,130 @@ +package workflow + +import ( + "fmt" + "maps" + "sort" + "strings" +) + +// LSPServerConfig defines a single language server entry under top-level frontmatter "lsp:". +type LSPServerConfig struct { + Command string `json:"command,omitempty"` + Args []string `json:"args,omitempty"` + FileExtensions map[string]string `json:"fileExtensions,omitempty"` +} + +// LSPManager handles LSP configuration normalization, validation, and generation. +type LSPManager struct { + servers map[string]LSPServerConfig +} + +func NewLSPManager(servers map[string]LSPServerConfig) *LSPManager { + normalized := make(map[string]LSPServerConfig, len(servers)) + for key, value := range servers { + language := strings.TrimSpace(strings.ToLower(key)) + if language == "" { + continue + } + config := value + config.Command = strings.TrimSpace(config.Command) + normalized[language] = config + } + return &LSPManager{servers: normalized} +} + +func (m *LSPManager) HasServers() bool { + return m != nil && len(m.servers) > 0 +} + +func (m *LSPManager) Validate() error { + if !m.HasServers() { + return nil + } + for language, config := range m.servers { + if config.Command == "" { + return fmt.Errorf("lsp.%s.command is required", language) + } + if len(config.FileExtensions) == 0 { + return fmt.Errorf("lsp.%s.fileExtensions must define at least one file extension mapping", language) + } + } + return nil +} + +func (m *LSPManager) CopilotLSPServers() map[string]LSPServerConfig { + if !m.HasServers() { + return nil + } + result := make(map[string]LSPServerConfig, len(m.servers)) + maps.Copy(result, m.servers) + return result +} + +func (m *LSPManager) GenerateInstallSteps() []GitHubActionStep { + if !m.HasServers() { + return nil + } + + langs := make([]string, 0, len(m.servers)) + for language := range m.servers { + langs = append(langs, language) + } + sort.Strings(langs) + + var steps []GitHubActionStep + for _, language := range langs { + spec, ok := lspInstallSpecs[language] + if !ok { + continue + } + step := GitHubActionStep{ + " - name: " + spec.StepName, + " run: " + spec.Command, + " timeout-minutes: 10", + } + steps = append(steps, step) + } + + return steps +} + +type lspInstallSpec struct { + StepName string + Command string +} + +var lspInstallSpecs = map[string]lspInstallSpec{ + "bash": { + StepName: "Install Bash LSP dependencies", + Command: "npm install -g bash-language-server", + }, + "go": { + StepName: "Install Go LSP dependencies", + Command: "go install golang.org/x/tools/gopls@latest", + }, + "php": { + StepName: "Install PHP LSP dependencies", + Command: "npm install -g intelephense", + }, + "python": { + StepName: "Install Python LSP dependencies", + Command: "npm install -g pyright", + }, + "ruby": { + StepName: "Install Ruby LSP dependencies", + Command: "gem install solargraph", + }, + "rust": { + StepName: "Install Rust LSP dependencies", + Command: "rustup component add rust-analyzer", + }, + "typescript": { + StepName: "Install TypeScript LSP dependencies", + Command: "npm install -g typescript typescript-language-server", + }, + "yaml": { + StepName: "Install YAML LSP dependencies", + Command: "npm install -g yaml-language-server", + }, +} diff --git a/pkg/workflow/lsp_manager_test.go b/pkg/workflow/lsp_manager_test.go new file mode 100644 index 00000000000..dcb772a33c0 --- /dev/null +++ b/pkg/workflow/lsp_manager_test.go @@ -0,0 +1,54 @@ +//go:build !integration + +package workflow + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestLSPManagerValidate(t *testing.T) { + manager := NewLSPManager(map[string]LSPServerConfig{ + "typescript": { + Command: "typescript-language-server", + Args: []string{"--stdio"}, + FileExtensions: map[string]string{ + ".ts": "typescript", + }, + }, + }) + require.NoError(t, manager.Validate()) + + invalid := NewLSPManager(map[string]LSPServerConfig{ + "python": { + Command: "pyright-langserver", + }, + }) + require.Error(t, invalid.Validate()) +} + +func TestLSPManagerGenerateInstallSteps(t *testing.T) { + manager := NewLSPManager(map[string]LSPServerConfig{ + "typescript": { + Command: "typescript-language-server", + FileExtensions: map[string]string{ + ".ts": "typescript", + }, + }, + "unknown": { + Command: "my-lsp", + FileExtensions: map[string]string{ + ".foo": "foo", + }, + }, + }) + + steps := manager.GenerateInstallSteps() + require.Len(t, steps, 1) + content := strings.Join(steps[0], "\n") + assert.Contains(t, content, "Install TypeScript LSP dependencies") + assert.Contains(t, content, "npm install -g typescript typescript-language-server") +} diff --git a/pkg/workflow/lsp_validation.go b/pkg/workflow/lsp_validation.go new file mode 100644 index 00000000000..11651e4f4f1 --- /dev/null +++ b/pkg/workflow/lsp_validation.go @@ -0,0 +1,28 @@ +package workflow + +import "fmt" + +func (c *Compiler) validateLSPSupport(workflowData *WorkflowData) error { + if workflowData == nil || len(workflowData.LSP) == 0 { + return nil + } + + manager := NewLSPManager(workflowData.LSP) + if err := manager.Validate(); err != nil { + return err + } + + engineID := workflowData.AI + if workflowData.EngineConfig != nil && workflowData.EngineConfig.ID != "" { + engineID = workflowData.EngineConfig.ID + } + if engineID == "" { + engineID = "copilot" + } + + if engineID != "copilot" { + return fmt.Errorf("lsp is currently only supported for engine: copilot (found engine: %s)", engineID) + } + + return nil +} diff --git a/pkg/workflow/lsp_validation_test.go b/pkg/workflow/lsp_validation_test.go new file mode 100644 index 00000000000..3083eb48548 --- /dev/null +++ b/pkg/workflow/lsp_validation_test.go @@ -0,0 +1,57 @@ +//go:build !integration + +package workflow + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestValidateLSPSupport(t *testing.T) { + compiler := NewCompiler() + + validLSP := map[string]LSPServerConfig{ + "typescript": { + Command: "typescript-language-server", + Args: []string{"--stdio"}, + FileExtensions: map[string]string{ + ".ts": "typescript", + }, + }, + } + + t.Run("copilot engine accepts lsp", func(t *testing.T) { + err := compiler.validateLSPSupport(&WorkflowData{ + AI: "copilot", + LSP: validLSP, + }) + require.NoError(t, err) + }) + + t.Run("non-copilot engine rejects lsp", func(t *testing.T) { + err := compiler.validateLSPSupport(&WorkflowData{ + AI: "codex", + LSP: validLSP, + }) + require.Error(t, err) + assert.Contains(t, err.Error(), "only supported for engine: copilot") + }) + + t.Run("invalid lsp config fails validation", func(t *testing.T) { + err := compiler.validateLSPSupport(&WorkflowData{ + AI: "copilot", + LSP: map[string]LSPServerConfig{ + "python": { + Command: "", + FileExtensions: map[string]string{ + ".py": "python", + }, + }, + }, + }) + require.Error(t, err) + assert.Contains(t, err.Error(), "lsp.python.command is required") + }) +} diff --git a/pkg/workflow/workflow_builder.go b/pkg/workflow/workflow_builder.go index 60a2f340af8..c9bf338e499 100644 --- a/pkg/workflow/workflow_builder.go +++ b/pkg/workflow/workflow_builder.go @@ -53,6 +53,7 @@ func (c *Compiler) buildInitialWorkflowData( IncludedFiles: toolsResult.allIncludedFiles, ImportInputs: importsResult.ImportInputs, Tools: toolsResult.tools, + LSP: extractLSPConfig(toolsResult.parsedFrontmatter, result.Frontmatter), ParsedTools: NewTools(toolsResult.tools), Runtimes: toolsResult.runtimes, RunInstallScripts: toolsResult.runInstallScripts, @@ -155,6 +156,32 @@ func (c *Compiler) buildInitialWorkflowData( return workflowData } +func extractLSPConfig(parsedFrontmatter *FrontmatterConfig, frontmatter map[string]any) map[string]LSPServerConfig { + if parsedFrontmatter != nil && len(parsedFrontmatter.LSP) > 0 { + return parsedFrontmatter.LSP + } + + rawLSP, ok := frontmatter["lsp"] + if !ok { + return nil + } + + jsonBytes, err := json.Marshal(rawLSP) + if err != nil { + return nil + } + + var lsp map[string]LSPServerConfig + if err := json.Unmarshal(jsonBytes, &lsp); err != nil { + return nil + } + + if len(lsp) == 0 { + return nil + } + return lsp +} + func extractMainModelCostsOverlay(toolsResult *toolsProcessingResult, frontmatter map[string]any) map[string]any { // Fall back to raw frontmatter when ParseFrontmatterConfig failed (e.g. due to unrecognized // tool config shapes like bash: ["*"]). From 8444c8e127e834fec595564eb63fb88e3625e27c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 26 Jun 2026 18:35:34 +0000 Subject: [PATCH 02/13] Improve LSP config diagnostics Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/workflow/lsp_manager.go | 5 +++++ pkg/workflow/workflow_builder.go | 2 ++ 2 files changed, 7 insertions(+) diff --git a/pkg/workflow/lsp_manager.go b/pkg/workflow/lsp_manager.go index 18ef355b923..22ac8285920 100644 --- a/pkg/workflow/lsp_manager.go +++ b/pkg/workflow/lsp_manager.go @@ -5,8 +5,12 @@ import ( "maps" "sort" "strings" + + "github.com/github/gh-aw/pkg/logger" ) +var lspManagerLog = logger.New("workflow:lsp_manager") + // LSPServerConfig defines a single language server entry under top-level frontmatter "lsp:". type LSPServerConfig struct { Command string `json:"command,omitempty"` @@ -24,6 +28,7 @@ func NewLSPManager(servers map[string]LSPServerConfig) *LSPManager { for key, value := range servers { language := strings.TrimSpace(strings.ToLower(key)) if language == "" { + lspManagerLog.Printf("Skipping invalid LSP language key: %q", key) continue } config := value diff --git a/pkg/workflow/workflow_builder.go b/pkg/workflow/workflow_builder.go index c9bf338e499..8bd374e877e 100644 --- a/pkg/workflow/workflow_builder.go +++ b/pkg/workflow/workflow_builder.go @@ -168,11 +168,13 @@ func extractLSPConfig(parsedFrontmatter *FrontmatterConfig, frontmatter map[stri jsonBytes, err := json.Marshal(rawLSP) if err != nil { + workflowBuilderLog.Printf("Failed to marshal lsp frontmatter config: %v", err) return nil } var lsp map[string]LSPServerConfig if err := json.Unmarshal(jsonBytes, &lsp); err != nil { + workflowBuilderLog.Printf("Failed to unmarshal lsp frontmatter config: %v", err) return nil } From 16f2a7731f4dc45d2346e3bf7b435ccb3e49c685 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 26 Jun 2026 22:24:59 +0000 Subject: [PATCH 03/13] feat(jsweep): add TypeScript LSP server to enable JavaScript/TypeScript code intelligence Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .github/workflows/jsweep.lock.yml | 7 +++++-- .github/workflows/jsweep.md | 10 ++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/.github/workflows/jsweep.lock.yml b/.github/workflows/jsweep.lock.yml index e89d2a9f517..1c175a3f971 100644 --- a/.github/workflows/jsweep.lock.yml +++ b/.github/workflows/jsweep.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"8b645f570da34a11144ef35f4f6c7f69b67aae1e5d4450cc2a9fd25befe0c12c","body_hash":"9ce30f5cb169db7ddfaefbc090c94365da3069e5f11b7d47e842a1046c70c8c1","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.65","copilot-sdk":"1.0.4"}} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"ebc1df2775f1711593bbaabfb5a951318f4318f5c15b3309ff5bc80a5f3e02e5","body_hash":"9ce30f5cb169db7ddfaefbc090c94365da3069e5f11b7d47e842a1046c70c8c1","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.65","copilot-sdk":"1.0.4"}} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_GRAFANA_AUTHORIZATION","GH_AW_OTEL_GRAFANA_ENDPOINT","GH_AW_OTEL_SENTRY_AUTHORIZATION","GH_AW_OTEL_SENTRY_ENDPOINT","GITHUB_TOKEN"],"actions":[{"repo":"actions/cache/restore","sha":"27d5ce7f107fe9357f9df03efb73ab90386fccae","version":"v5.0.5"},{"repo":"actions/cache/save","sha":"27d5ce7f107fe9357f9df03efb73ab90386fccae","version":"v5.0.5"},{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7.0.0"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.11","digest":"sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.11@sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.11","digest":"sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.11@sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d"},{"image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.11"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.11","digest":"sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.11@sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.30","digest":"sha256:35625d1a2269b1238606078c879f59a91cffc4ac33eb54bf39c6418822c1a8be","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.30@sha256:35625d1a2269b1238606078c879f59a91cffc4ac33eb54bf39c6418822c1a8be"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.4.0","digest":"sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036","pinned_image":"ghcr.io/github/github-mcp-server:v1.4.0@sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036"}]} # This file was automatically generated by gh-aw. DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # @@ -534,6 +534,9 @@ jobs: run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.27.11 --rootless - name: Install GitHub Copilot SDK (Node.js) run: cd "${GITHUB_WORKSPACE}" && npm install --ignore-scripts --no-save @github/copilot-sdk@1.0.4 + - name: Install TypeScript LSP dependencies + run: npm install -g typescript typescript-language-server + timeout-minutes: 10 - name: Determine automatic lockdown mode for GitHub MCP Server id: determine-automatic-lockdown uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 (source v9) @@ -831,7 +834,7 @@ jobs: printf '%s' "$(date +%s%3N)" > /tmp/gh-aw/agent_cli_start_ms.txt trap 'rm -f "$HOME/.copilot/settings.json"' EXIT mkdir -p "$HOME/.copilot" - printf '%s' '{"builtInAgents":{"rubberDuck":false}}' > "$HOME/.copilot/settings.json" + printf '%s' '{"builtInAgents":{"rubberDuck":false},"lspServers":{"typescript":{"command":"typescript-language-server","args":["--stdio"],"fileExtensions":{".cjs":"javascript",".js":"javascript",".mjs":"javascript",".ts":"typescript",".tsx":"typescriptreact"}}}}' > "$HOME/.copilot/settings.json" export XDG_CONFIG_HOME="$HOME" export GH_AW_MCP_CONFIG="$HOME/.copilot/mcp-config.json" touch /tmp/gh-aw/agent-step-summary.md diff --git a/.github/workflows/jsweep.md b/.github/workflows/jsweep.md index 232e1fa46b8..af8c05d309e 100644 --- a/.github/workflows/jsweep.md +++ b/.github/workflows/jsweep.md @@ -25,6 +25,16 @@ tools: edit: bash: ["*"] cache-memory: true +lsp: + typescript: + command: typescript-language-server + args: ["--stdio"] + fileExtensions: + ".js": javascript + ".cjs": javascript + ".mjs": javascript + ".ts": typescript + ".tsx": typescriptreact steps: - name: Install Node.js dependencies working-directory: actions/setup/js From 018f27871a328923adac342af6c2f61d1bc0da5f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 26 Jun 2026 23:05:21 +0000 Subject: [PATCH 04/13] docs: add .github/aw/lsp.md instructions for LSP frontmatter configuration Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .github/aw/lsp.md | 166 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 166 insertions(+) create mode 100644 .github/aw/lsp.md diff --git a/.github/aw/lsp.md b/.github/aw/lsp.md new file mode 100644 index 00000000000..14e8bf54791 --- /dev/null +++ b/.github/aw/lsp.md @@ -0,0 +1,166 @@ +--- +description: Language Server Protocol (LSP) configuration reference for gh-aw Copilot workflows — frontmatter syntax, supported servers, and file extension mapping. +--- + +# LSP Configuration + +The `lsp` frontmatter field lets Copilot-engine workflows declare language servers. At compile time, the compiler: + +1. Validates the configuration and rejects the workflow if `lsp` is used with a non-Copilot engine. +2. Generates `~/.copilot/settings.json` with an `lspServers` block the Copilot CLI reads at startup. +3. Injects install steps for known server ecosystems into the agent setup job. + +> ⚠️ **`lsp` is only supported with `engine: copilot`**. Using it with any other engine causes a compile-time error. + +## Syntax + +```yaml +engine: + id: copilot + +lsp: + : + command: + args: [, ] # optional + fileExtensions: + ".": # at least one required +``` + +Each key under `lsp` is a language identifier (lowercase, alphanumeric, hyphens, or underscores). It maps to a server definition with: + +| Field | Required | Description | +|---|---|---| +| `command` | **yes** | Executable name or path for the language server | +| `args` | no | Command-line arguments passed to the server on startup | +| `fileExtensions` | **yes** | Map of file extension (with leading `.`) to LSP language ID | + +## Built-in Servers + +For the languages below, the compiler automatically injects an install step — no manual `steps:` entry is needed. + +| Language key | Install command | Example `command` | +|---|---|---| +| `bash` | `npm install -g bash-language-server` | `bash-language-server` | +| `go` | `go install golang.org/x/tools/gopls@latest` | `gopls` | +| `php` | `npm install -g intelephense` | `intelephense` | +| `python` | `npm install -g pyright` | `pyright-langserver` | +| `ruby` | `gem install solargraph` | `solargraph` | +| `rust` | `rustup component add rust-analyzer` | `rust-analyzer` | +| `typescript` | `npm install -g typescript typescript-language-server` | `typescript-language-server` | +| `yaml` | `npm install -g yaml-language-server` | `yaml-language-server` | + +Language keys not in this table still work — the compiler simply skips the auto-install step. Add a manual `steps:` entry to install the server yourself. + +## Examples + +### TypeScript / JavaScript + +```yaml +engine: + id: copilot + +lsp: + typescript: + command: typescript-language-server + args: ["--stdio"] + fileExtensions: + ".ts": typescript + ".tsx": typescriptreact + ".js": javascript + ".cjs": javascript + ".mjs": javascript +``` + +### Python + +```yaml +engine: + id: copilot + +lsp: + python: + command: pyright-langserver + args: ["--stdio"] + fileExtensions: + ".py": python +``` + +### Go + +```yaml +engine: + id: copilot + +lsp: + go: + command: gopls + fileExtensions: + ".go": go +``` + +### Multiple Languages + +```yaml +engine: + id: copilot + +lsp: + typescript: + command: typescript-language-server + args: ["--stdio"] + fileExtensions: + ".ts": typescript + ".js": javascript + python: + command: pyright-langserver + args: ["--stdio"] + fileExtensions: + ".py": python +``` + +### Custom Server (no built-in install) + +For servers without a built-in install spec, add a manual install step: + +```yaml +engine: + id: copilot + +steps: + - name: Install custom language server + run: npm install -g my-custom-language-server + +lsp: + mylang: + command: my-custom-language-server + args: ["--stdio"] + fileExtensions: + ".ml": mylang +``` + +## Network Requirements + +Installing LSP servers requires network access to the appropriate package registry. Add the matching ecosystem to `network.allowed`: + +| Language | Ecosystem to add | +|---|---| +| `bash`, `php`, `python`, `typescript`, `yaml` | `node` | +| `go` | `go` | +| `ruby` | `ruby` | +| `rust` | `rust` | + +```yaml +network: + allowed: + - node # for npm-installed servers (typescript, yaml, python/pyright, etc.) + - go # for gopls +``` + +## Compile-time Validation + +The compiler enforces these rules at compile time: + +- `lsp` requires `engine: copilot` — any other engine causes an error. +- Each language entry must have a non-empty `command`. +- Each language entry must define at least one `fileExtensions` mapping. +- Language keys are case-insensitive and trimmed; duplicate keys that collapse to the same lowercase value cause nondeterministic behavior and should be avoided. From 2379f247573d8e15c3961d5dff39db9caaea22c5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 26 Jun 2026 23:50:38 +0000 Subject: [PATCH 05/13] fix(lsp): deterministic key normalization, use ResolveEngineID, register lsp.md in skill - lsp_manager.go: sort input keys before normalizing so duplicate keys (e.g. 'TypeScript' vs 'typescript') always resolve to the same winner deterministically; log collisions explicitly instead of silently losing entries. - lsp_validation.go: replace inline engine-ID resolution with the shared ResolveEngineID helper to avoid future drift. - lsp_manager_test.go: add TestLSPManagerDuplicateKeyNormalization. - pkg/cli/data/agentic_workflows_fallback_aw_files.json: add lsp.md. - .github/skills/agentic-workflows/SKILL.md: regenerate to include lsp.md. Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com> --- .github/skills/agentic-workflows/SKILL.md | 1 + .../agentic_workflows_fallback_aw_files.json | 1 + pkg/workflow/lsp_manager.go | 18 ++++++++++++-- pkg/workflow/lsp_manager_test.go | 24 +++++++++++++++++++ pkg/workflow/lsp_validation.go | 5 +--- 5 files changed, 43 insertions(+), 6 deletions(-) diff --git a/.github/skills/agentic-workflows/SKILL.md b/.github/skills/agentic-workflows/SKILL.md index ee714d339d8..b218f40f122 100644 --- a/.github/skills/agentic-workflows/SKILL.md +++ b/.github/skills/agentic-workflows/SKILL.md @@ -28,6 +28,7 @@ Load these files from `github/gh-aw` (they are not available locally). - `.github/aw/github-agentic-workflows.md` - `.github/aw/github-mcp-server.md` - `.github/aw/llms.md` +- `.github/aw/lsp.md` - `.github/aw/mcp-clis.md` - `.github/aw/memory.md` - `.github/aw/messages.md` diff --git a/pkg/cli/data/agentic_workflows_fallback_aw_files.json b/pkg/cli/data/agentic_workflows_fallback_aw_files.json index d56b0e2142e..e88dab1aee6 100644 --- a/pkg/cli/data/agentic_workflows_fallback_aw_files.json +++ b/pkg/cli/data/agentic_workflows_fallback_aw_files.json @@ -16,6 +16,7 @@ "github-agentic-workflows.md", "github-mcp-server.md", "llms.md", + "lsp.md", "mcp-clis.md", "memory.md", "messages.md", diff --git a/pkg/workflow/lsp_manager.go b/pkg/workflow/lsp_manager.go index 22ac8285920..f4225f5f794 100644 --- a/pkg/workflow/lsp_manager.go +++ b/pkg/workflow/lsp_manager.go @@ -24,14 +24,28 @@ type LSPManager struct { } func NewLSPManager(servers map[string]LSPServerConfig) *LSPManager { + // Sort keys for deterministic normalization order so that when two keys + // collapse to the same lowercase value (e.g. "TypeScript" and "typescript"), + // the lexicographically first original key always wins and the duplicate is + // logged rather than silently lost. + keys := make([]string, 0, len(servers)) + for k := range servers { + keys = append(keys, k) + } + sort.Strings(keys) + normalized := make(map[string]LSPServerConfig, len(servers)) - for key, value := range servers { + for _, key := range keys { language := strings.TrimSpace(strings.ToLower(key)) if language == "" { lspManagerLog.Printf("Skipping invalid LSP language key: %q", key) continue } - config := value + if _, exists := normalized[language]; exists { + lspManagerLog.Printf("Duplicate LSP language key %q (normalizes to %q): entry ignored", key, language) + continue + } + config := servers[key] config.Command = strings.TrimSpace(config.Command) normalized[language] = config } diff --git a/pkg/workflow/lsp_manager_test.go b/pkg/workflow/lsp_manager_test.go index dcb772a33c0..023e13f8102 100644 --- a/pkg/workflow/lsp_manager_test.go +++ b/pkg/workflow/lsp_manager_test.go @@ -30,6 +30,30 @@ func TestLSPManagerValidate(t *testing.T) { require.Error(t, invalid.Validate()) } +func TestLSPManagerDuplicateKeyNormalization(t *testing.T) { + // "TypeScript" and "typescript" both normalize to "typescript". + // Sorted order puts "TypeScript" first (uppercase < lowercase in ASCII), + // so the "TypeScript" entry should win deterministically. + manager := NewLSPManager(map[string]LSPServerConfig{ + "TypeScript": { + Command: "first-server", + FileExtensions: map[string]string{ + ".ts": "typescript", + }, + }, + "typescript": { + Command: "second-server", + FileExtensions: map[string]string{ + ".ts": "typescript", + }, + }, + }) + + servers := manager.CopilotLSPServers() + require.Len(t, servers, 1) + assert.Equal(t, "first-server", servers["typescript"].Command) +} + func TestLSPManagerGenerateInstallSteps(t *testing.T) { manager := NewLSPManager(map[string]LSPServerConfig{ "typescript": { diff --git a/pkg/workflow/lsp_validation.go b/pkg/workflow/lsp_validation.go index 11651e4f4f1..80f023f9b39 100644 --- a/pkg/workflow/lsp_validation.go +++ b/pkg/workflow/lsp_validation.go @@ -12,10 +12,7 @@ func (c *Compiler) validateLSPSupport(workflowData *WorkflowData) error { return err } - engineID := workflowData.AI - if workflowData.EngineConfig != nil && workflowData.EngineConfig.ID != "" { - engineID = workflowData.EngineConfig.ID - } + engineID := ResolveEngineID(workflowData) if engineID == "" { engineID = "copilot" } From 45a42b434b5eeca33006e44b6e7640bf8e250b98 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 26 Jun 2026 23:52:47 +0000 Subject: [PATCH 06/13] chore: recompile detection-analysis-report workflow after main merge Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com> --- .github/workflows/detection-analysis-report.lock.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/detection-analysis-report.lock.yml b/.github/workflows/detection-analysis-report.lock.yml index 8cb736b2378..8ddf13926d4 100644 --- a/.github/workflows/detection-analysis-report.lock.yml +++ b/.github/workflows/detection-analysis-report.lock.yml @@ -796,6 +796,7 @@ jobs: GH_AW_ASSETS_ALLOWED_EXTS: ${{ env.GH_AW_ASSETS_ALLOWED_EXTS }} GH_AW_ASSETS_BRANCH: ${{ env.GH_AW_ASSETS_BRANCH }} GH_AW_ASSETS_MAX_SIZE_KB: ${{ env.GH_AW_ASSETS_MAX_SIZE_KB }} + GH_AW_POLICY_ALLOW_CREATE_PULL_REQUEST: ${{ vars.GH_AW_POLICY_ALLOW_CREATE_PULL_REQUEST || 'true' }} GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS_CONFIG_PATH }} GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS_TOOLS_PATH }} @@ -829,10 +830,10 @@ jobs: * ) DOCKER_SOCK_PATH=/var/run/docker.sock ;; esac DOCKER_SOCK_GID=$(stat -c '%g' "$DOCKER_SOCK_PATH" 2>/dev/null || echo '0') - export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network bridge -p 127.0.0.1:'"${MCP_GATEWAY_PORT}"':'"${MCP_GATEWAY_PORT}"' --name awmg-mcpg --user '"${MCP_GATEWAY_UID}"':'"${MCP_GATEWAY_GID}"' --group-add '"${DOCKER_SOCK_GID}"' -v '"${DOCKER_SOCK_PATH}"':/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DOCKER_HOST=unix:///var/run/docker.sock -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e RUNNER_TEMP -e GITHUB_AW_OTEL_TRACE_ID -e GITHUB_AW_OTEL_PARENT_SPAN_ID -e OTEL_EXPORTER_OTLP_HEADERS -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw -v '"${RUNNER_TEMP}"'/gh-aw/safeoutputs:'"${RUNNER_TEMP}"'/gh-aw/safeoutputs:rw ghcr.io/github/gh-aw-mcpg:v0.3.30' + export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network bridge -p 127.0.0.1:'"${MCP_GATEWAY_PORT}"':'"${MCP_GATEWAY_PORT}"' --name awmg-mcpg --user '"${MCP_GATEWAY_UID}"':'"${MCP_GATEWAY_GID}"' --group-add '"${DOCKER_SOCK_GID}"' -v '"${DOCKER_SOCK_PATH}"':/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DOCKER_HOST=unix:///var/run/docker.sock -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_POLICY_ALLOW_CREATE_PULL_REQUEST -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e RUNNER_TEMP -e GITHUB_AW_OTEL_TRACE_ID -e GITHUB_AW_OTEL_PARENT_SPAN_ID -e OTEL_EXPORTER_OTLP_HEADERS -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw -v '"${RUNNER_TEMP}"'/gh-aw/safeoutputs:'"${RUNNER_TEMP}"'/gh-aw/safeoutputs:rw ghcr.io/github/gh-aw-mcpg:v0.3.30' GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node) - cat << GH_AW_MCP_CONFIG_467ea9a369748bce_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" + cat << GH_AW_MCP_CONFIG_971d4d777021cc7e_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" { "mcpServers": { "agenticworkflows": { @@ -884,6 +885,7 @@ jobs: "GH_AW_SAFE_OUTPUTS": "\${GH_AW_SAFE_OUTPUTS}", "GH_AW_SAFE_OUTPUTS_CONFIG_PATH": "\${GH_AW_SAFE_OUTPUTS_CONFIG_PATH}", "GH_AW_SAFE_OUTPUTS_TOOLS_PATH": "\${GH_AW_SAFE_OUTPUTS_TOOLS_PATH}", + "GH_AW_POLICY_ALLOW_CREATE_PULL_REQUEST": "\${GH_AW_POLICY_ALLOW_CREATE_PULL_REQUEST}", "GITHUB_REPOSITORY": "\${GITHUB_REPOSITORY}", "GITHUB_TOKEN": "\${GITHUB_TOKEN}", "GITHUB_WORKSPACE": "\${GITHUB_WORKSPACE}", @@ -910,7 +912,7 @@ jobs: } } } - GH_AW_MCP_CONFIG_467ea9a369748bce_EOF + GH_AW_MCP_CONFIG_971d4d777021cc7e_EOF - name: Mount MCP servers as CLIs id: mount-mcp-clis continue-on-error: true From 2d891536d8c8a7b3ab5310c1ee7467d69beda5f2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 27 Jun 2026 01:43:34 +0000 Subject: [PATCH 07/13] fix(lsp): add --ignore-scripts to npm LSP install commands Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .github/aw/lsp.md | 10 +++++----- pkg/workflow/copilot_engine_test.go | 2 +- pkg/workflow/lsp_manager.go | 10 +++++----- pkg/workflow/lsp_manager_test.go | 2 +- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/aw/lsp.md b/.github/aw/lsp.md index 14e8bf54791..6449ede38c2 100644 --- a/.github/aw/lsp.md +++ b/.github/aw/lsp.md @@ -40,14 +40,14 @@ For the languages below, the compiler automatically injects an install step — | Language key | Install command | Example `command` | |---|---|---| -| `bash` | `npm install -g bash-language-server` | `bash-language-server` | +| `bash` | `npm install -g --ignore-scripts bash-language-server` | `bash-language-server` | | `go` | `go install golang.org/x/tools/gopls@latest` | `gopls` | -| `php` | `npm install -g intelephense` | `intelephense` | -| `python` | `npm install -g pyright` | `pyright-langserver` | +| `php` | `npm install -g --ignore-scripts intelephense` | `intelephense` | +| `python` | `npm install -g --ignore-scripts pyright` | `pyright-langserver` | | `ruby` | `gem install solargraph` | `solargraph` | | `rust` | `rustup component add rust-analyzer` | `rust-analyzer` | -| `typescript` | `npm install -g typescript typescript-language-server` | `typescript-language-server` | -| `yaml` | `npm install -g yaml-language-server` | `yaml-language-server` | +| `typescript` | `npm install -g --ignore-scripts typescript typescript-language-server` | `typescript-language-server` | +| `yaml` | `npm install -g --ignore-scripts yaml-language-server` | `yaml-language-server` | Language keys not in this table still work — the compiler simply skips the auto-install step. Add a manual `steps:` entry to install the server yourself. diff --git a/pkg/workflow/copilot_engine_test.go b/pkg/workflow/copilot_engine_test.go index 91c83609a17..7199bcca871 100644 --- a/pkg/workflow/copilot_engine_test.go +++ b/pkg/workflow/copilot_engine_test.go @@ -310,7 +310,7 @@ func TestCopilotEngineInstallationSteps_WithLSPConfig(t *testing.T) { if !strings.Contains(allLinesStr, "Install Python LSP dependencies") { t.Fatalf("Expected Python LSP install step, got:\n%s", allLinesStr) } - if !strings.Contains(allLinesStr, "npm install -g pyright") { + if !strings.Contains(allLinesStr, "npm install -g --ignore-scripts pyright") { t.Fatalf("Expected pyright install command, got:\n%s", allLinesStr) } } diff --git a/pkg/workflow/lsp_manager.go b/pkg/workflow/lsp_manager.go index f4225f5f794..60985a8b63f 100644 --- a/pkg/workflow/lsp_manager.go +++ b/pkg/workflow/lsp_manager.go @@ -116,7 +116,7 @@ type lspInstallSpec struct { var lspInstallSpecs = map[string]lspInstallSpec{ "bash": { StepName: "Install Bash LSP dependencies", - Command: "npm install -g bash-language-server", + Command: "npm install -g --ignore-scripts bash-language-server", }, "go": { StepName: "Install Go LSP dependencies", @@ -124,11 +124,11 @@ var lspInstallSpecs = map[string]lspInstallSpec{ }, "php": { StepName: "Install PHP LSP dependencies", - Command: "npm install -g intelephense", + Command: "npm install -g --ignore-scripts intelephense", }, "python": { StepName: "Install Python LSP dependencies", - Command: "npm install -g pyright", + Command: "npm install -g --ignore-scripts pyright", }, "ruby": { StepName: "Install Ruby LSP dependencies", @@ -140,10 +140,10 @@ var lspInstallSpecs = map[string]lspInstallSpec{ }, "typescript": { StepName: "Install TypeScript LSP dependencies", - Command: "npm install -g typescript typescript-language-server", + Command: "npm install -g --ignore-scripts typescript typescript-language-server", }, "yaml": { StepName: "Install YAML LSP dependencies", - Command: "npm install -g yaml-language-server", + Command: "npm install -g --ignore-scripts yaml-language-server", }, } diff --git a/pkg/workflow/lsp_manager_test.go b/pkg/workflow/lsp_manager_test.go index 023e13f8102..2fe70233f49 100644 --- a/pkg/workflow/lsp_manager_test.go +++ b/pkg/workflow/lsp_manager_test.go @@ -74,5 +74,5 @@ func TestLSPManagerGenerateInstallSteps(t *testing.T) { require.Len(t, steps, 1) content := strings.Join(steps[0], "\n") assert.Contains(t, content, "Install TypeScript LSP dependencies") - assert.Contains(t, content, "npm install -g typescript typescript-language-server") + assert.Contains(t, content, "npm install -g --ignore-scripts typescript typescript-language-server") } From 3af6d454a873dc27fc10be9f850188185341771f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 27 Jun 2026 03:54:50 +0000 Subject: [PATCH 08/13] feat(lsp): delegate LSP runtime setup to runtime manager for proper SHA pinning LSP servers that need Go, Ruby, or Node.js should have their runtime set up via the standard runtime manager (DetectRuntimeRequirements / GenerateRuntimeSetupSteps), which emits properly SHA-pinned setup actions (e.g. actions/setup-go, ruby/setup-ruby). Previously the runtimes were relied upon being pre-installed on the runner with no version pinning. Changes: - Add RuntimeID field to lspInstallSpec to declare which runtime each LSP server needs (node/go/ruby; empty for rust which has no runtime manager entry) - Add RuntimeRequirements() method on LSPManager that returns []RuntimeRequirement for runtimes needed by configured LSP servers, deduplicating by runtime ID - Add detectFromLSPServers() in runtime_detection.go and call it from DetectRuntimeRequirements so runtime setup steps for LSP-required runtimes are emitted alongside other runtime setup steps - Add comprehensive tests covering per-language runtime IDs, dedup, and integration with DetectRuntimeRequirements - Recompile jsweep.lock.yml to include --ignore-scripts flag Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .github/workflows/jsweep.lock.yml | 2 +- pkg/workflow/lsp_manager.go | 96 +++++++++++++++++++++++++------ pkg/workflow/lsp_manager_test.go | 84 +++++++++++++++++++++++++++ pkg/workflow/runtime_detection.go | 26 +++++++++ 4 files changed, 189 insertions(+), 19 deletions(-) diff --git a/.github/workflows/jsweep.lock.yml b/.github/workflows/jsweep.lock.yml index b98bae0428e..9d200911e54 100644 --- a/.github/workflows/jsweep.lock.yml +++ b/.github/workflows/jsweep.lock.yml @@ -535,7 +535,7 @@ jobs: - name: Install GitHub Copilot SDK (Node.js) run: cd "${GITHUB_WORKSPACE}" && npm install --ignore-scripts --no-save @github/copilot-sdk@1.0.4 - name: Install TypeScript LSP dependencies - run: npm install -g typescript typescript-language-server + run: npm install -g --ignore-scripts typescript typescript-language-server timeout-minutes: 10 - name: Determine automatic lockdown mode for GitHub MCP Server id: determine-automatic-lockdown diff --git a/pkg/workflow/lsp_manager.go b/pkg/workflow/lsp_manager.go index 60985a8b63f..589529f7ea8 100644 --- a/pkg/workflow/lsp_manager.go +++ b/pkg/workflow/lsp_manager.go @@ -108,42 +108,102 @@ func (m *LSPManager) GenerateInstallSteps() []GitHubActionStep { return steps } +// RuntimeRequirements returns the set of runtime requirements for all configured LSP +// servers. These are returned as [RuntimeRequirement] values so that the caller can +// feed them into the standard runtime manager (DetectRuntimeRequirements / +// GenerateRuntimeSetupSteps), which emits properly SHA-pinned setup actions. +// +// Only languages that have a matching entry in knownRuntimes are included; languages +// whose runtime is not tracked by the runtime manager (e.g. "rust") are silently +// skipped — their install commands still appear in GenerateInstallSteps. +// +// Note: Node.js-based LSP servers (bash, php, python, typescript, yaml) map to the +// "node" runtime, but the Copilot engine already sets up Node.js unconditionally via +// BuildNpmEngineInstallStepsWithAWF. Returning "node" here is correct and harmless: +// DetectRuntimeRequirements deduplicates by runtime ID, so at most one Node.js setup +// step is emitted regardless of how many node-based LSP servers are configured. +func (m *LSPManager) RuntimeRequirements() []RuntimeRequirement { + if !m.HasServers() { + return nil + } + + seen := make(map[string]bool) + var result []RuntimeRequirement + + langs := make([]string, 0, len(m.servers)) + for language := range m.servers { + langs = append(langs, language) + } + sort.Strings(langs) + + for _, language := range langs { + spec, ok := lspInstallSpecs[language] + if !ok || spec.RuntimeID == "" { + continue + } + if seen[spec.RuntimeID] { + continue + } + seen[spec.RuntimeID] = true + runtime := findRuntimeByID(spec.RuntimeID) + if runtime == nil { + lspManagerLog.Printf("LSP language %q specifies unknown runtime ID %q; skipping runtime requirement", language, spec.RuntimeID) + continue + } + result = append(result, RuntimeRequirement{ + Runtime: runtime, + Version: "", + Cooldown: true, + }) + } + return result +} + type lspInstallSpec struct { - StepName string - Command string + StepName string + Command string + RuntimeID string // runtime manager ID for the runtime needed to run this LSP server } var lspInstallSpecs = map[string]lspInstallSpec{ "bash": { - StepName: "Install Bash LSP dependencies", - Command: "npm install -g --ignore-scripts bash-language-server", + StepName: "Install Bash LSP dependencies", + Command: "npm install -g --ignore-scripts bash-language-server", + RuntimeID: "node", }, "go": { - StepName: "Install Go LSP dependencies", - Command: "go install golang.org/x/tools/gopls@latest", + StepName: "Install Go LSP dependencies", + Command: "go install golang.org/x/tools/gopls@latest", + RuntimeID: "go", }, "php": { - StepName: "Install PHP LSP dependencies", - Command: "npm install -g --ignore-scripts intelephense", + StepName: "Install PHP LSP dependencies", + Command: "npm install -g --ignore-scripts intelephense", + RuntimeID: "node", }, "python": { - StepName: "Install Python LSP dependencies", - Command: "npm install -g --ignore-scripts pyright", + StepName: "Install Python LSP dependencies", + Command: "npm install -g --ignore-scripts pyright", + RuntimeID: "node", }, "ruby": { - StepName: "Install Ruby LSP dependencies", - Command: "gem install solargraph", + StepName: "Install Ruby LSP dependencies", + Command: "gem install solargraph", + RuntimeID: "ruby", }, "rust": { - StepName: "Install Rust LSP dependencies", - Command: "rustup component add rust-analyzer", + StepName: "Install Rust LSP dependencies", + Command: "rustup component add rust-analyzer", + RuntimeID: "", // Rust is not in knownRuntimes; runtime setup is done via rustup }, "typescript": { - StepName: "Install TypeScript LSP dependencies", - Command: "npm install -g --ignore-scripts typescript typescript-language-server", + StepName: "Install TypeScript LSP dependencies", + Command: "npm install -g --ignore-scripts typescript typescript-language-server", + RuntimeID: "node", }, "yaml": { - StepName: "Install YAML LSP dependencies", - Command: "npm install -g --ignore-scripts yaml-language-server", + StepName: "Install YAML LSP dependencies", + Command: "npm install -g --ignore-scripts yaml-language-server", + RuntimeID: "node", }, } diff --git a/pkg/workflow/lsp_manager_test.go b/pkg/workflow/lsp_manager_test.go index 2fe70233f49..9e522aea391 100644 --- a/pkg/workflow/lsp_manager_test.go +++ b/pkg/workflow/lsp_manager_test.go @@ -76,3 +76,87 @@ func TestLSPManagerGenerateInstallSteps(t *testing.T) { assert.Contains(t, content, "Install TypeScript LSP dependencies") assert.Contains(t, content, "npm install -g --ignore-scripts typescript typescript-language-server") } + +func TestLSPManagerRuntimeRequirements_NodeBased(t *testing.T) { + // Node.js-based LSP servers (typescript, python/pyright, bash, php, yaml) should all + // resolve to the "node" runtime — deduplicated to a single requirement. + manager := NewLSPManager(map[string]LSPServerConfig{ + "typescript": {Command: "typescript-language-server", FileExtensions: map[string]string{".ts": "typescript"}}, + "python": {Command: "pyright-langserver", FileExtensions: map[string]string{".py": "python"}}, + }) + reqs := manager.RuntimeRequirements() + require.Len(t, reqs, 1, "typescript and python both need node; expect exactly one node requirement") + assert.Equal(t, "node", reqs[0].Runtime.ID) +} + +func TestLSPManagerRuntimeRequirements_GoLSP(t *testing.T) { + // gopls requires the Go runtime. + manager := NewLSPManager(map[string]LSPServerConfig{ + "go": {Command: "gopls", FileExtensions: map[string]string{".go": "go"}}, + }) + reqs := manager.RuntimeRequirements() + require.Len(t, reqs, 1) + assert.Equal(t, "go", reqs[0].Runtime.ID) +} + +func TestLSPManagerRuntimeRequirements_RubyLSP(t *testing.T) { + // solargraph requires the Ruby runtime. + manager := NewLSPManager(map[string]LSPServerConfig{ + "ruby": {Command: "solargraph", FileExtensions: map[string]string{".rb": "ruby"}}, + }) + reqs := manager.RuntimeRequirements() + require.Len(t, reqs, 1) + assert.Equal(t, "ruby", reqs[0].Runtime.ID) +} + +func TestLSPManagerRuntimeRequirements_RustLSP(t *testing.T) { + // rust-analyzer uses rustup; Rust is not in knownRuntimes so no runtime requirement is returned. + manager := NewLSPManager(map[string]LSPServerConfig{ + "rust": {Command: "rust-analyzer", FileExtensions: map[string]string{".rs": "rust"}}, + }) + reqs := manager.RuntimeRequirements() + assert.Empty(t, reqs, "Rust is not in knownRuntimes; expect no runtime requirement") +} + +func TestLSPManagerRuntimeRequirements_UnknownLanguage(t *testing.T) { + // A language not in lspInstallSpecs contributes no runtime requirement. + manager := NewLSPManager(map[string]LSPServerConfig{ + "cobol": {Command: "cobol-lsp", FileExtensions: map[string]string{".cbl": "cobol"}}, + }) + reqs := manager.RuntimeRequirements() + assert.Empty(t, reqs) +} + +func TestLSPManagerRuntimeRequirements_MultipleRuntimes(t *testing.T) { + // A workflow with both a Go LSP and a TypeScript LSP needs both Go and Node.js. + manager := NewLSPManager(map[string]LSPServerConfig{ + "go": {Command: "gopls", FileExtensions: map[string]string{".go": "go"}}, + "typescript": {Command: "typescript-language-server", FileExtensions: map[string]string{".ts": "typescript"}}, + }) + reqs := manager.RuntimeRequirements() + require.Len(t, reqs, 2) + ids := map[string]bool{} + for _, r := range reqs { + ids[r.Runtime.ID] = true + } + assert.True(t, ids["go"], "expected go runtime requirement") + assert.True(t, ids["node"], "expected node runtime requirement") +} + +func TestDetectRuntimeRequirements_LSPServers(t *testing.T) { + // DetectRuntimeRequirements should pick up runtime requirements from LSP config. + data := &WorkflowData{ + LSP: map[string]LSPServerConfig{ + "go": {Command: "gopls", FileExtensions: map[string]string{".go": "go"}}, + }, + } + reqs := DetectRuntimeRequirements(data) + found := false + for _, r := range reqs { + if r.Runtime.ID == "go" { + found = true + break + } + } + assert.True(t, found, "expected Go runtime requirement from LSP config") +} diff --git a/pkg/workflow/runtime_detection.go b/pkg/workflow/runtime_detection.go index 254e965945c..f0afcd21821 100644 --- a/pkg/workflow/runtime_detection.go +++ b/pkg/workflow/runtime_detection.go @@ -52,6 +52,15 @@ func DetectRuntimeRequirements(workflowData *WorkflowData) []RuntimeRequirement } } + // Detect runtimes required by LSP server configurations. + // Each known LSP server declares the runtime it needs (e.g. "go" for gopls, + // "ruby" for solargraph). Feeding these through the runtime manager ensures + // the runtime is set up via a properly SHA-pinned setup action rather than + // relying on whatever version happens to be pre-installed on the runner. + if len(workflowData.LSP) > 0 { + detectFromLSPServers(workflowData.LSP, requirements) + } + // Apply runtime overrides from frontmatter if workflowData.Runtimes != nil { applyRuntimeOverrides(workflowData.Runtimes, requirements) @@ -237,6 +246,23 @@ func detectFromMCPScripts(mcpScripts *MCPScriptsConfig, requirements map[string] } } +// detectFromLSPServers adds runtime requirements for the runtimes needed by configured +// LSP servers. Each entry in lspInstallSpecs declares the runtime ID it needs (e.g. +// "go" for gopls, "ruby" for solargraph, "node" for npm-based servers). Feeding these +// through the runtime manager ensures that the runtime is set up via a properly +// SHA-pinned setup action (e.g. actions/setup-go, ruby/setup-ruby) and with a pinned +// version, rather than relying on whatever happens to be pre-installed on the runner. +func detectFromLSPServers(lsp map[string]LSPServerConfig, requirements map[string]*RuntimeRequirement) { + if len(lsp) == 0 { + return + } + manager := NewLSPManager(lsp) + for _, req := range manager.RuntimeRequirements() { + runtimeSetupLog.Printf("LSP server requires runtime: %s", req.Runtime.ID) + updateRequiredRuntime(req.Runtime, req.Version, requirements) + } +} + // updateRequiredRuntime updates the version requirement, choosing the highest version func updateRequiredRuntime(runtime *Runtime, newVersion string, requirements map[string]*RuntimeRequirement) { existing, exists := requirements[runtime.ID] From 553ec35371d37f5b17bd9708d6285a9420cff5a9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 27 Jun 2026 05:44:59 +0000 Subject: [PATCH 09/13] feat(lsp): align LSP npm package installation with runtime manager Respect runtimes.node.run-install-scripts and runtimes.node.cooldown settings when generating LSP server install steps, consistent with how the runtime manager generates npm install commands. Changes: - Add NpmPackages []string to lspInstallSpec; update all npm-based specs (bash, php, python, typescript, yaml) to use it instead of hardcoded command strings - Keep raw Command field for non-npm runtimes (go, gem, rustup) - GenerateInstallSteps now accepts *WorkflowData and reads runInstallScripts from workflowData.RunInstallScripts and cooldown from resolveRuntimeCooldown(workflowData, 'node') - --ignore-scripts is omitted when run-install-scripts is true; NPM_CONFIG_MIN_RELEASE_AGE is injected when cooldown is enabled - appendCopilotLSPInstallSteps passes workflowData to GenerateInstallSteps - Tests updated; add coverage for run-install-scripts and cooldown-disabled - Recompile jsweep.lock.yml (TypeScript LSP step now includes cooldown env) Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .github/workflows/jsweep.lock.yml | 2 + pkg/workflow/copilot_engine_installation.go | 2 +- pkg/workflow/lsp_manager.go | 93 ++++++++++++++++----- pkg/workflow/lsp_manager_test.go | 49 ++++++++++- 4 files changed, 121 insertions(+), 25 deletions(-) diff --git a/.github/workflows/jsweep.lock.yml b/.github/workflows/jsweep.lock.yml index 9d200911e54..e578514a4c6 100644 --- a/.github/workflows/jsweep.lock.yml +++ b/.github/workflows/jsweep.lock.yml @@ -536,6 +536,8 @@ jobs: run: cd "${GITHUB_WORKSPACE}" && npm install --ignore-scripts --no-save @github/copilot-sdk@1.0.4 - name: Install TypeScript LSP dependencies run: npm install -g --ignore-scripts typescript typescript-language-server + env: + NPM_CONFIG_MIN_RELEASE_AGE: '3' timeout-minutes: 10 - name: Determine automatic lockdown mode for GitHub MCP Server id: determine-automatic-lockdown diff --git a/pkg/workflow/copilot_engine_installation.go b/pkg/workflow/copilot_engine_installation.go index dba60b88ce8..59a27dedf5e 100644 --- a/pkg/workflow/copilot_engine_installation.go +++ b/pkg/workflow/copilot_engine_installation.go @@ -139,7 +139,7 @@ func appendCopilotLSPInstallSteps(steps []GitHubActionStep, workflowData *Workfl return steps } manager := NewLSPManager(workflowData.LSP) - lspSteps := manager.GenerateInstallSteps() + lspSteps := manager.GenerateInstallSteps(workflowData) if len(lspSteps) == 0 { return steps } diff --git a/pkg/workflow/lsp_manager.go b/pkg/workflow/lsp_manager.go index 589529f7ea8..9bba7f88972 100644 --- a/pkg/workflow/lsp_manager.go +++ b/pkg/workflow/lsp_manager.go @@ -80,11 +80,33 @@ func (m *LSPManager) CopilotLSPServers() map[string]LSPServerConfig { return result } -func (m *LSPManager) GenerateInstallSteps() []GitHubActionStep { +// GenerateInstallSteps generates GitHub Actions steps that install the LSP server +// binary dependencies for all configured LSP servers. +// +// For npm-based servers the generated install command respects the workflow's +// runtime-manager settings: +// - workflowData.RunInstallScripts (runtimes.node.run-install-scripts) controls +// whether --ignore-scripts is omitted (default: scripts disabled). +// - resolveRuntimeCooldown (runtimes.node.cooldown) controls whether +// NPM_CONFIG_MIN_RELEASE_AGE is injected (default: cooldown enabled). +// +// Pass nil for workflowData to get secure defaults (--ignore-scripts, cooldown on). +func (m *LSPManager) GenerateInstallSteps(workflowData *WorkflowData) []GitHubActionStep { if !m.HasServers() { return nil } + // Determine npm install flags from runtime-manager settings. + // Defaults match the runtime manager's secure defaults: + // - --ignore-scripts ON (supply-chain protection) + // - cooldown ON (NPM_CONFIG_MIN_RELEASE_AGE) + runInstallScripts := false + cooldownEnabled := true + if workflowData != nil { + runInstallScripts = workflowData.RunInstallScripts + cooldownEnabled = resolveRuntimeCooldown(workflowData, "node") + } + langs := make([]string, 0, len(m.servers)) for language := range m.servers { langs = append(langs, language) @@ -97,10 +119,34 @@ func (m *LSPManager) GenerateInstallSteps() []GitHubActionStep { if !ok { continue } - step := GitHubActionStep{ - " - name: " + spec.StepName, - " run: " + spec.Command, - " timeout-minutes: 10", + + var step GitHubActionStep + if len(spec.NpmPackages) > 0 { + // npm-based LSP server: build install command from runtime-manager settings. + args := []string{"npm", "install", "-g"} + if !runInstallScripts { + args = append(args, "--ignore-scripts") + } + args = append(args, spec.NpmPackages...) + installCmd := strings.Join(args, " ") + step = GitHubActionStep{ + " - name: " + spec.StepName, + " run: " + installCmd, + } + if cooldownEnabled { + step = append(step, + " env:", + fmt.Sprintf(" NPM_CONFIG_MIN_RELEASE_AGE: '%d'", npmDefaultCooldownDays), + ) + } + step = append(step, " timeout-minutes: 10") + } else { + // Non-npm LSP server (go install, gem install, rustup): use raw command. + step = GitHubActionStep{ + " - name: " + spec.StepName, + " run: " + spec.Command, + " timeout-minutes: 10", + } } steps = append(steps, step) } @@ -160,16 +206,17 @@ func (m *LSPManager) RuntimeRequirements() []RuntimeRequirement { } type lspInstallSpec struct { - StepName string - Command string - RuntimeID string // runtime manager ID for the runtime needed to run this LSP server + StepName string + NpmPackages []string // Non-nil: install these packages globally via npm (respects RunInstallScripts + cooldown) + Command string // Non-empty: raw install command for non-npm runtimes (go, gem, rustup) + RuntimeID string // runtime manager ID for the runtime needed to run this LSP server } var lspInstallSpecs = map[string]lspInstallSpec{ "bash": { - StepName: "Install Bash LSP dependencies", - Command: "npm install -g --ignore-scripts bash-language-server", - RuntimeID: "node", + StepName: "Install Bash LSP dependencies", + NpmPackages: []string{"bash-language-server"}, + RuntimeID: "node", }, "go": { StepName: "Install Go LSP dependencies", @@ -177,14 +224,14 @@ var lspInstallSpecs = map[string]lspInstallSpec{ RuntimeID: "go", }, "php": { - StepName: "Install PHP LSP dependencies", - Command: "npm install -g --ignore-scripts intelephense", - RuntimeID: "node", + StepName: "Install PHP LSP dependencies", + NpmPackages: []string{"intelephense"}, + RuntimeID: "node", }, "python": { - StepName: "Install Python LSP dependencies", - Command: "npm install -g --ignore-scripts pyright", - RuntimeID: "node", + StepName: "Install Python LSP dependencies", + NpmPackages: []string{"pyright"}, + RuntimeID: "node", }, "ruby": { StepName: "Install Ruby LSP dependencies", @@ -197,13 +244,13 @@ var lspInstallSpecs = map[string]lspInstallSpec{ RuntimeID: "", // Rust is not in knownRuntimes; runtime setup is done via rustup }, "typescript": { - StepName: "Install TypeScript LSP dependencies", - Command: "npm install -g --ignore-scripts typescript typescript-language-server", - RuntimeID: "node", + StepName: "Install TypeScript LSP dependencies", + NpmPackages: []string{"typescript", "typescript-language-server"}, + RuntimeID: "node", }, "yaml": { - StepName: "Install YAML LSP dependencies", - Command: "npm install -g --ignore-scripts yaml-language-server", - RuntimeID: "node", + StepName: "Install YAML LSP dependencies", + NpmPackages: []string{"yaml-language-server"}, + RuntimeID: "node", }, } diff --git a/pkg/workflow/lsp_manager_test.go b/pkg/workflow/lsp_manager_test.go index 9e522aea391..866f541a5a0 100644 --- a/pkg/workflow/lsp_manager_test.go +++ b/pkg/workflow/lsp_manager_test.go @@ -70,11 +70,58 @@ func TestLSPManagerGenerateInstallSteps(t *testing.T) { }, }) - steps := manager.GenerateInstallSteps() + // Default: --ignore-scripts + cooldown enabled + steps := manager.GenerateInstallSteps(nil) require.Len(t, steps, 1) content := strings.Join(steps[0], "\n") assert.Contains(t, content, "Install TypeScript LSP dependencies") assert.Contains(t, content, "npm install -g --ignore-scripts typescript typescript-language-server") + assert.Contains(t, content, "NPM_CONFIG_MIN_RELEASE_AGE") +} + +func TestLSPManagerGenerateInstallSteps_RunInstallScripts(t *testing.T) { + // When run-install-scripts is enabled, --ignore-scripts must be omitted. + manager := NewLSPManager(map[string]LSPServerConfig{ + "typescript": { + Command: "typescript-language-server", + FileExtensions: map[string]string{ + ".ts": "typescript", + }, + }, + }) + workflowData := &WorkflowData{ + RunInstallScripts: true, + } + steps := manager.GenerateInstallSteps(workflowData) + require.Len(t, steps, 1) + content := strings.Join(steps[0], "\n") + assert.Contains(t, content, "npm install -g typescript typescript-language-server") + assert.NotContains(t, content, "--ignore-scripts") +} + +func TestLSPManagerGenerateInstallSteps_CooldownDisabled(t *testing.T) { + // When runtimes.node.cooldown: false, NPM_CONFIG_MIN_RELEASE_AGE must not appear. + manager := NewLSPManager(map[string]LSPServerConfig{ + "yaml": { + Command: "yaml-language-server", + FileExtensions: map[string]string{ + ".yaml": "yaml", + }, + }, + }) + falseVal := false + workflowData := &WorkflowData{ + ParsedFrontmatter: &FrontmatterConfig{ + RuntimesTyped: &RuntimesConfig{ + Node: &RuntimeConfig{Cooldown: &falseVal}, + }, + }, + } + steps := manager.GenerateInstallSteps(workflowData) + require.Len(t, steps, 1) + content := strings.Join(steps[0], "\n") + assert.Contains(t, content, "--ignore-scripts") + assert.NotContains(t, content, "NPM_CONFIG_MIN_RELEASE_AGE") } func TestLSPManagerRuntimeRequirements_NodeBased(t *testing.T) { From af19dabe29f9c994b60bee657d20ed9b671fe519 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 27 Jun 2026 14:56:01 +0000 Subject: [PATCH 10/13] feat(lsp): mark lsp as experimental with compile-time warning, schema description, and docs update Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .github/aw/lsp.md | 2 + pkg/parser/schemas/main_workflow_schema.json | 2 +- pkg/workflow/compiler_validators.go | 1 + pkg/workflow/lsp_experimental_warning_test.go | 103 ++++++++++++++++++ 4 files changed, 107 insertions(+), 1 deletion(-) create mode 100644 pkg/workflow/lsp_experimental_warning_test.go diff --git a/.github/aw/lsp.md b/.github/aw/lsp.md index 6449ede38c2..8898bbbf995 100644 --- a/.github/aw/lsp.md +++ b/.github/aw/lsp.md @@ -4,6 +4,8 @@ description: Language Server Protocol (LSP) configuration reference for gh-aw Co # LSP Configuration +> ⚠️ **Experimental feature.** The `lsp` frontmatter field is experimental. Using it will emit a compile-time warning. The interface may change in future releases. + The `lsp` frontmatter field lets Copilot-engine workflows declare language servers. At compile time, the compiler: 1. Validates the configuration and rejects the workflow if `lsp` is used with a non-Copilot engine. diff --git a/pkg/parser/schemas/main_workflow_schema.json b/pkg/parser/schemas/main_workflow_schema.json index 2c95e869794..6b423d373a6 100644 --- a/pkg/parser/schemas/main_workflow_schema.json +++ b/pkg/parser/schemas/main_workflow_schema.json @@ -4712,7 +4712,7 @@ }, "lsp": { "type": "object", - "description": "Top-level Language Server Protocol (LSP) configuration for Copilot CLI. Each key is a language identifier and each value defines the server command, args, and file extension mappings.", + "description": "⚠️ Experimental. Top-level Language Server Protocol (LSP) configuration for Copilot CLI. Each key is a language identifier and each value defines the server command, args, and file extension mappings. Using this field emits a compile-time warning.", "patternProperties": { "^[a-zA-Z0-9_-]+$": { "type": "object", diff --git a/pkg/workflow/compiler_validators.go b/pkg/workflow/compiler_validators.go index 4a0a529e9a4..29fa8f21f84 100644 --- a/pkg/workflow/compiler_validators.go +++ b/pkg/workflow/compiler_validators.go @@ -304,6 +304,7 @@ func (c *Compiler) emitExperimentalFeatureWarnings(workflowData *WorkflowData) { {enabled: workflowData.SafeOutputs != nil && workflowData.SafeOutputs.ReplaceLabel != nil, message: "Using experimental feature: replace-label"}, {enabled: workflowData.EngineConfig != nil && workflowData.EngineConfig.CopilotSDK, message: "Using experimental feature: engine.copilot-sdk"}, {enabled: isFeatureEnabled(constants.GHAWDetectionFeatureFlag, workflowData), message: "Using experimental feature: gh-aw-detection"}, + {enabled: len(workflowData.LSP) > 0, message: "Using experimental feature: lsp"}, } for _, warning := range warnings { if warning.enabled { diff --git a/pkg/workflow/lsp_experimental_warning_test.go b/pkg/workflow/lsp_experimental_warning_test.go new file mode 100644 index 00000000000..5a84a2a9ca8 --- /dev/null +++ b/pkg/workflow/lsp_experimental_warning_test.go @@ -0,0 +1,103 @@ +//go:build integration + +package workflow + +import ( + "bytes" + "io" + "os" + "path/filepath" + "strings" + "testing" + + "github.com/github/gh-aw/pkg/testutil" +) + +// TestLSPExperimentalWarning tests that using the lsp frontmatter field +// emits an experimental feature warning at compile time. +func TestLSPExperimentalWarning(t *testing.T) { + tests := []struct { + name string + content string + expectWarning bool + }{ + { + name: "lsp field produces experimental warning", + content: `--- +on: workflow_dispatch +engine: copilot +permissions: + contents: read +lsp: + typescript: + command: typescript-language-server + args: ["--stdio"] + fileExtensions: + ".ts": typescript +--- + +# Test Workflow +`, + expectWarning: true, + }, + { + name: "no lsp field does not produce experimental warning", + content: `--- +on: workflow_dispatch +engine: copilot +permissions: + contents: read +--- + +# Test Workflow +`, + expectWarning: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tmpDir := testutil.TempDir(t, "lsp-experimental-warning-test") + + testFile := filepath.Join(tmpDir, "test-workflow.md") + if err := os.WriteFile(testFile, []byte(tt.content), 0644); err != nil { + t.Fatal(err) + } + + oldStderr := os.Stderr + r, w, _ := os.Pipe() + os.Stderr = w + + compiler := NewCompiler() + compiler.SetStrictMode(false) + err := compiler.CompileWorkflow(testFile) + + w.Close() + os.Stderr = oldStderr + var buf bytes.Buffer + io.Copy(&buf, r) + stderrOutput := buf.String() + + if err != nil { + t.Errorf("Expected compilation to succeed but it failed: %v", err) + return + } + + expectedMessage := "Using experimental feature: lsp" + + if tt.expectWarning { + if !strings.Contains(stderrOutput, expectedMessage) { + t.Errorf("Expected warning containing '%s', got stderr:\n%s", expectedMessage, stderrOutput) + } + if compiler.GetWarningCount() == 0 { + t.Error("Expected warning count > 0 but got 0") + } + return + } + + if strings.Contains(stderrOutput, expectedMessage) { + t.Errorf("Did not expect warning '%s', but got stderr:\n%s", expectedMessage, stderrOutput) + } + }) + } +} From 4aa65ef55bebba18a8c3e47d7769d1ed6dc2ad17 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 27 Jun 2026 17:48:38 +0000 Subject: [PATCH 11/13] feat(smoke-codex): enable TypeScript LSP and add function-count LSP test - Change engine from codex to copilot with copilot-sdk: true to enable LSP support (LSP is Copilot-only per compiler validation) - Add lsp.typescript frontmatter with typescript-language-server covering .js, .cjs, .mjs, .ts, .tsx extensions - Add Node.js runtime (v20) required by the TypeScript LSP server - Add test #11: use LSP document symbols to count functions in actions/setup/js/safe_output_helpers.cjs and report >=1 as pass - Update issue body reference to include test #11 in results - Recompile: lock.yml updated with lspServers settings.json payload, npm install --ignore-scripts typescript typescript-language-server, and NPM_CONFIG_MIN_RELEASE_AGE cooldown env var Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .github/workflows/smoke-codex.lock.yml | 423 ++++++++++--------------- .github/workflows/smoke-codex.md | 22 +- 2 files changed, 188 insertions(+), 257 deletions(-) diff --git a/.github/workflows/smoke-codex.lock.yml b/.github/workflows/smoke-codex.lock.yml index f345d13cd44..b574c550584 100644 --- a/.github/workflows/smoke-codex.lock.yml +++ b/.github/workflows/smoke-codex.lock.yml @@ -1,5 +1,5 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"42a605ac96d9044f6b6da08a4ebdf7db4c1c2f261b7660d34fabd9d0e9701da0","body_hash":"2148439cf2a717e019b1235ae583d7f6087ecc038accee7a93e3b54b3c24c6b9","strict":true,"agent_id":"codex","engine_versions":{"codex":"0.142.1"}} -# gh-aw-manifest: {"version":1,"secrets":["CODEX_API_KEY","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_GRAFANA_AUTHORIZATION","GH_AW_OTEL_GRAFANA_ENDPOINT","GH_AW_OTEL_SENTRY_AUTHORIZATION","GH_AW_OTEL_SENTRY_ENDPOINT","GITHUB_TOKEN","OPENAI_API_KEY"],"actions":[{"repo":"actions-ecosystem/action-add-labels","sha":"c96b68fec76a0987cd93957189e9abd0b9a72ff1","version":"v1.1.3"},{"repo":"actions/cache/restore","sha":"27d5ce7f107fe9357f9df03efb73ab90386fccae","version":"v5.0.5"},{"repo":"actions/cache/save","sha":"27d5ce7f107fe9357f9df03efb73ab90386fccae","version":"v5.0.5"},{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7.0.0"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-go","sha":"4a3601121dd01d1626a1e23e37211e3254c1c06c","version":"v6.4.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.11","digest":"sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.11@sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.11","digest":"sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.11@sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d"},{"image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.11"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.11","digest":"sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.11@sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.30","digest":"sha256:35625d1a2269b1238606078c879f59a91cffc4ac33eb54bf39c6418822c1a8be","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.30@sha256:35625d1a2269b1238606078c879f59a91cffc4ac33eb54bf39c6418822c1a8be"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.4.0","digest":"sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036","pinned_image":"ghcr.io/github/github-mcp-server:v1.4.0@sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036"},{"image":"ghcr.io/github/serena-mcp-server:latest","digest":"sha256:bf343399e3725c45528f531a230f3a04521d4cdef29f9a5af6282ff0d3c393c5","pinned_image":"ghcr.io/github/serena-mcp-server:latest@sha256:bf343399e3725c45528f531a230f3a04521d4cdef29f9a5af6282ff0d3c393c5"}]} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"f66218c1af651228b4ec3d19d0e98c5f28dd35a1030de8e39f23ec226b031511","body_hash":"ec2ee9864ce0d5f39c4d5c9c529fd3eeb4758bfdf8b426936f7e8126973fb1ab","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.65","copilot-sdk":"1.0.4"}} +# gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_GRAFANA_AUTHORIZATION","GH_AW_OTEL_GRAFANA_ENDPOINT","GH_AW_OTEL_SENTRY_AUTHORIZATION","GH_AW_OTEL_SENTRY_ENDPOINT","GITHUB_TOKEN"],"actions":[{"repo":"actions-ecosystem/action-add-labels","sha":"c96b68fec76a0987cd93957189e9abd0b9a72ff1","version":"v1.1.3"},{"repo":"actions/cache/restore","sha":"27d5ce7f107fe9357f9df03efb73ab90386fccae","version":"v5.0.5"},{"repo":"actions/cache/save","sha":"27d5ce7f107fe9357f9df03efb73ab90386fccae","version":"v5.0.5"},{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7.0.0"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-go","sha":"4a3601121dd01d1626a1e23e37211e3254c1c06c","version":"v6.4.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.11","digest":"sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.11@sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.11","digest":"sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.11@sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d"},{"image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.11"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.11","digest":"sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.11@sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.30","digest":"sha256:35625d1a2269b1238606078c879f59a91cffc4ac33eb54bf39c6418822c1a8be","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.30@sha256:35625d1a2269b1238606078c879f59a91cffc4ac33eb54bf39c6418822c1a8be"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.4.0","digest":"sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036","pinned_image":"ghcr.io/github/github-mcp-server:v1.4.0@sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036"},{"image":"ghcr.io/github/serena-mcp-server:latest","digest":"sha256:bf343399e3725c45528f531a230f3a04521d4cdef29f9a5af6282ff0d3c393c5","pinned_image":"ghcr.io/github/serena-mcp-server:latest@sha256:bf343399e3725c45528f531a230f3a04521d4cdef29f9a5af6282ff0d3c393c5"}]} # This file was automatically generated by gh-aw. DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # # ___ _ _ @@ -36,7 +36,7 @@ # - shared/trufflehog.md # # Secrets used: -# - CODEX_API_KEY +# - COPILOT_GITHUB_TOKEN # - GH_AW_GITHUB_MCP_SERVER_TOKEN # - GH_AW_GITHUB_TOKEN # - GH_AW_OTEL_GRAFANA_AUTHORIZATION @@ -44,7 +44,6 @@ # - GH_AW_OTEL_SENTRY_AUTHORIZATION # - GH_AW_OTEL_SENTRY_ENDPOINT # - GITHUB_TOKEN -# - OPENAI_API_KEY # # Custom actions used: # - actions-ecosystem/action-add-labels@c96b68fec76a0987cd93957189e9abd0b9a72ff1 # v1.1.3 @@ -93,7 +92,7 @@ run-name: "Smoke Codex" env: OTEL_EXPORTER_OTLP_ENDPOINT: ${{ secrets.GH_AW_OTEL_SENTRY_ENDPOINT }} OTEL_SERVICE_NAME: gh-aw.smoke-codex - OTEL_RESOURCE_ATTRIBUTES: 'gh-aw.workflow.name=Smoke%20Codex,gh-aw.repository=${{ github.repository }},gh-aw.run.id=${{ github.run_id }},github.run_id=${{ github.run_id }},gh-aw.engine.id=codex' + OTEL_RESOURCE_ATTRIBUTES: 'gh-aw.workflow.name=Smoke%20Codex,gh-aw.repository=${{ github.repository }},gh-aw.run.id=${{ github.run_id }},github.run_id=${{ github.run_id }},gh-aw.engine.id=copilot' OTEL_EXPORTER_OTLP_HEADERS: x-sentry-auth=${{ secrets.GH_AW_OTEL_SENTRY_AUTHORIZATION }} GH_AW_OTLP_ALL_HEADERS: x-sentry-auth=${{ secrets.GH_AW_OTEL_SENTRY_AUTHORIZATION }},Authorization=${{ secrets.GH_AW_OTEL_GRAFANA_AUTHORIZATION }} GH_AW_OTLP_ENDPOINTS: '[{"url":"${{ secrets.GH_AW_OTEL_SENTRY_ENDPOINT }}","headers":"x-sentry-auth=${{ secrets.GH_AW_OTEL_SENTRY_AUTHORIZATION }}"},{"url":"${{ secrets.GH_AW_OTEL_GRAFANA_ENDPOINT }}","headers":"Authorization=${{ secrets.GH_AW_OTEL_GRAFANA_AUTHORIZATION }}"}]' @@ -152,19 +151,19 @@ jobs: env: GH_AW_SETUP_WORKFLOW_NAME: "Smoke Codex" GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/smoke-codex.lock.yml@${{ github.ref }} - GH_AW_INFO_VERSION: "0.142.1" + GH_AW_INFO_VERSION: "1.0.65" GH_AW_INFO_AWF_VERSION: "v0.27.11" - GH_AW_INFO_ENGINE_ID: "codex" + GH_AW_INFO_ENGINE_ID: "copilot" - name: Mask OTLP telemetry headers run: bash "${RUNNER_TEMP}/gh-aw/actions/mask_otlp_headers.sh" - name: Generate agentic run info id: generate_aw_info env: - GH_AW_INFO_ENGINE_ID: "codex" - GH_AW_INFO_ENGINE_NAME: "Codex" - GH_AW_INFO_MODEL: ${{ vars.GH_AW_MODEL_AGENT_CODEX || vars.GH_AW_DEFAULT_MODEL_CODEX || 'gpt-5.4' }} - GH_AW_INFO_VERSION: "0.142.1" - GH_AW_INFO_AGENT_VERSION: "0.142.1" + GH_AW_INFO_ENGINE_ID: "copilot" + GH_AW_INFO_ENGINE_NAME: "GitHub Copilot CLI" + GH_AW_INFO_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || vars.GH_AW_DEFAULT_MODEL_COPILOT || 'claude-sonnet-4.6' }} + GH_AW_INFO_VERSION: "1.0.65" + GH_AW_INFO_AGENT_VERSION: "1.0.65" GH_AW_INFO_WORKFLOW_NAME: "Smoke Codex" GH_AW_INFO_EXPERIMENTAL: "false" GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true" @@ -240,12 +239,11 @@ jobs: setupGlobals(core, github, context, exec, io, getOctokit); const { main } = require('${{ runner.temp }}/gh-aw/actions/add_reaction.cjs'); await main(); - - name: Validate CODEX_API_KEY or OPENAI_API_KEY secret + - name: Validate COPILOT_GITHUB_TOKEN secret id: validate-secret - run: bash "${RUNNER_TEMP}/gh-aw/actions/validate_multi_secret.sh" CODEX_API_KEY OPENAI_API_KEY Codex https://github.github.com/gh-aw/reference/engines/#openai-codex + run: bash "${RUNNER_TEMP}/gh-aw/actions/validate_multi_secret.sh" COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default env: - CODEX_API_KEY: ${{ secrets.CODEX_API_KEY }} - OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} - name: Checkout .github and .agents folders uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: @@ -285,7 +283,7 @@ jobs: id: sanitized uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: - GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,*.grafana.net,*.sentry.io,127.0.0.1,172.30.0.1,::1,api.github.com,api.openai.com,api.snapcraft.io,app.renovatebot.com,appveyor.com,archive.ubuntu.com,azure.archive.ubuntu.com,badgen.net,cdn.playwright.dev,chatgpt.com,circleci.com,codacy.com,codeclimate.com,codecov.io,codeload.github.com,coveralls.io,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,deepsource.io,docs.github.com,drone.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,go.dev,golang.org,goproxy.io,host.docker.internal,img.shields.io,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,localhost,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,openai.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,patch-diff.githubusercontent.com,pkg.go.dev,playwright.download.prss.microsoft.com,ppa.launchpad.net,proxy.golang.org,raw.githubusercontent.com,readthedocs.io,readthedocs.org,renovatebot.com,s.symcb.com,s.symcd.com,security.ubuntu.com,semaphoreci.com,shields.io,snyk.io,sonarcloud.io,sonarqube.com,storage.googleapis.com,sum.golang.org,travis-ci.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com" + GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,*.grafana.net,*.sentry.io,127.0.0.1,::1,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,app.renovatebot.com,appveyor.com,archive.ubuntu.com,azure.archive.ubuntu.com,badgen.net,bun.sh,cdn.jsdelivr.net,cdn.playwright.dev,circleci.com,codacy.com,codeclimate.com,codecov.io,codeload.github.com,coveralls.io,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,deb.nodesource.com,deepsource.io,deno.land,docs.github.com,drone.io,esm.sh,get.pnpm.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,go.dev,golang.org,googleapis.deno.dev,googlechromelabs.github.io,goproxy.io,host.docker.internal,img.shields.io,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,lfs.github.com,localhost,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,patch-diff.githubusercontent.com,pkg.go.dev,playwright.download.prss.microsoft.com,ppa.launchpad.net,proxy.golang.org,raw.githubusercontent.com,readthedocs.io,readthedocs.org,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,renovatebot.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,semaphoreci.com,shields.io,skimdb.npmjs.com,snyk.io,sonarcloud.io,sonarqube.com,storage.googleapis.com,sum.golang.org,telemetry.enterprise.githubcopilot.com,telemetry.vercel.com,travis-ci.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com,www.npmjs.com,www.npmjs.org,yarnpkg.com" with: script: | const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); @@ -411,7 +409,7 @@ jobs: uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - GH_AW_ENGINE_ID: "codex" + GH_AW_ENGINE_ID: "copilot" GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} @@ -498,8 +496,8 @@ jobs: /tmp/gh-aw/aw-prompts/prompt-import-tree.json /tmp/gh-aw/github_rate_limits.jsonl /tmp/gh-aw/base - /tmp/gh-aw/.codex/agents - /tmp/gh-aw/.codex/skills + /tmp/gh-aw/.github/agents + /tmp/gh-aw/.github/skills if-no-files-found: ignore retention-days: 1 @@ -559,9 +557,9 @@ jobs: env: GH_AW_SETUP_WORKFLOW_NAME: "Smoke Codex" GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/smoke-codex.lock.yml@${{ github.ref }} - GH_AW_INFO_VERSION: "0.142.1" + GH_AW_INFO_VERSION: "1.0.65" GH_AW_INFO_AWF_VERSION: "v0.27.11" - GH_AW_INFO_ENGINE_ID: "codex" + GH_AW_INFO_ENGINE_ID: "copilot" - name: Set runtime paths id: set-runtime-paths run: | @@ -584,6 +582,11 @@ jobs: cache: false - name: Capture GOROOT for AWF chroot mode run: echo "GOROOT=$(go env GOROOT)" >> "$GITHUB_ENV" + - name: Setup Node.js + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + with: + node-version: '20' + package-manager-cache: false - name: Create gh-aw temp directory run: bash "${RUNNER_TEMP}/gh-aw/actions/create_gh_aw_tmp_dir.sh" - name: Configure gh CLI for GitHub Enterprise @@ -626,15 +629,19 @@ jobs: setupGlobals(core, github, context, exec, io, getOctokit); const { main } = require('${{ runner.temp }}/gh-aw/actions/checkout_pr_branch.cjs'); await main(); - - name: Setup Node.js - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 - with: - node-version: '24' - package-manager-cache: false - - name: Install Codex CLI - run: npm install --ignore-scripts -g @openai/codex@0.142.1 + - name: Install GitHub Copilot CLI + run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" 1.0.65 + env: + GH_HOST: github.com - name: Install AWF binary run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.27.11 + - name: Install GitHub Copilot SDK (Node.js) + run: cd "${GITHUB_WORKSPACE}" && npm install --ignore-scripts --no-save @github/copilot-sdk@1.0.4 + - name: Install TypeScript LSP dependencies + run: npm install -g --ignore-scripts typescript typescript-language-server + env: + NPM_CONFIG_MIN_RELEASE_AGE: '3' + timeout-minutes: 10 - name: Install Playwright CLI run: npm install -g @playwright/cli@0.1.14 env: @@ -677,12 +684,12 @@ jobs: run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_base_github_folders.sh" - name: Restore inline sub-agents from activation artifact env: - GH_AW_SUB_AGENT_DIR: ".codex/agents" - GH_AW_SUB_AGENT_EXT: ".md" + GH_AW_SUB_AGENT_DIR: ".github/agents" + GH_AW_SUB_AGENT_EXT: ".agent.md" run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_inline_sub_agents.sh" - name: Restore inline skills from activation artifact env: - GH_AW_SKILL_DIR: ".codex/skills" + GH_AW_SKILL_DIR: ".github/skills" run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_inline_skills.sh" - name: Download container images run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.27.11@sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7 ghcr.io/github/gh-aw-firewall/api-proxy:0.27.11@sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.11 ghcr.io/github/gh-aw-firewall/squid:0.27.11@sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d ghcr.io/github/gh-aw-mcpg:v0.3.30@sha256:35625d1a2269b1238606078c879f59a91cffc4ac33eb54bf39c6418822c1a8be ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b ghcr.io/github/github-mcp-server:v1.4.0@sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036 ghcr.io/github/serena-mcp-server:latest@sha256:bf343399e3725c45528f531a230f3a04521d4cdef29f9a5af6282ff0d3c393c5 @@ -1025,7 +1032,6 @@ jobs: - name: Start MCP Gateway id: start-mcp-gateway env: - CODEX_HOME: /tmp/gh-aw/mcp-config GH_AW_POLICY_ALLOW_CREATE_PULL_REQUEST: ${{ vars.GH_AW_POLICY_ALLOW_CREATE_PULL_REQUEST || 'true' }} GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS_CONFIG_PATH }} @@ -1047,7 +1053,7 @@ jobs: export MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD="524288" export DEBUG="*" - export GH_AW_ENGINE="codex" + export GH_AW_ENGINE="copilot" export GH_AW_MCP_CLI_SERVERS='["safeoutputs","serena"]' MCP_GATEWAY_UID=$(id -u 2>/dev/null || echo '0') MCP_GATEWAY_GID=$(id -g 2>/dev/null || echo '0') @@ -1057,51 +1063,15 @@ jobs: * ) DOCKER_SOCK_PATH=/var/run/docker.sock ;; esac DOCKER_SOCK_GID=$(stat -c '%g' "$DOCKER_SOCK_PATH" 2>/dev/null || echo '0') - export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host --name awmg-mcpg --add-host host.docker.internal:127.0.0.1 --user '"${MCP_GATEWAY_UID}"':'"${MCP_GATEWAY_GID}"' --group-add '"${DOCKER_SOCK_GID}"' -v '"${DOCKER_SOCK_PATH}"':/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DOCKER_HOST=unix:///var/run/docker.sock -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_POLICY_ALLOW_CREATE_PULL_REQUEST -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e RUNNER_TEMP -e GITHUB_AW_OTEL_TRACE_ID -e GITHUB_AW_OTEL_PARENT_SPAN_ID -e OTEL_EXPORTER_OTLP_HEADERS -e CODEX_HOME -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw -v '"${RUNNER_TEMP}"'/gh-aw/safeoutputs:'"${RUNNER_TEMP}"'/gh-aw/safeoutputs:rw ghcr.io/github/gh-aw-mcpg:v0.3.30' - - cat > "${RUNNER_TEMP}/gh-aw/mcp-config/config.toml" << GH_AW_MCP_CONFIG_88a23f25b9e52e24_EOF - [history] - persistence = "none" - - [shell_environment_policy] - inherit = "core" - include_only = ["^CODEX_API_KEY$", "^GH_AW_ASSETS_ALLOWED_EXTS$", "^GH_AW_ASSETS_BRANCH$", "^GH_AW_ASSETS_MAX_SIZE_KB$", "^GH_AW_SAFE_OUTPUTS$", "^GITHUB_REPOSITORY$", "^GITHUB_SERVER_URL$", "^HOME$", "^OPENAI_API_KEY$", "^PATH$"] - - [mcp_servers.safeoutputs] - container = "ghcr.io/github/gh-aw-node" - mounts = ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw", "${RUNNER_TEMP}/gh-aw/safeoutputs:${RUNNER_TEMP}/gh-aw/safeoutputs:rw", "/tmp/gh-aw:/tmp/gh-aw:rw"] - args = ["-w", "$GITHUB_WORKSPACE"] - entrypoint = "sh" - entrypointArgs = ["-c", "sh ${RUNNER_TEMP}/gh-aw/safeoutputs/start_safe_outputs_mcp.sh"] - env_vars = ["DEBUG", "DEFAULT_BRANCH", "GH_AW_ASSETS_ALLOWED_EXTS", "GH_AW_ASSETS_BRANCH", "GH_AW_ASSETS_MAX_SIZE_KB", "GH_AW_MCP_LOG_DIR", "GH_AW_SAFE_OUTPUTS", "GH_AW_SAFE_OUTPUTS_CONFIG_PATH", "GH_AW_SAFE_OUTPUTS_TOOLS_PATH", "GH_AW_POLICY_ALLOW_CREATE_PULL_REQUEST", "GITHUB_REPOSITORY", "GITHUB_TOKEN", "GITHUB_WORKSPACE", "RUNNER_TEMP"] - - [mcp_servers.safeoutputs."guard-policies"] - - [mcp_servers.safeoutputs."guard-policies".write-sink] - accept = ["*"] - - [mcp_servers.serena] - container = "ghcr.io/github/serena-mcp-server:latest" - entrypoint = "serena" - entrypointArgs = ["start-mcp-server", "--context", "codex", "--project", "\${GITHUB_WORKSPACE}"] - mounts = ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw"] - args = [ - "--network", - "host", - ] - - [mcp_servers.serena."guard-policies"] - - [mcp_servers.serena."guard-policies".write-sink] - accept = ["*"] - GH_AW_MCP_CONFIG_88a23f25b9e52e24_EOF + export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host --name awmg-mcpg --add-host host.docker.internal:127.0.0.1 --user '"${MCP_GATEWAY_UID}"':'"${MCP_GATEWAY_GID}"' --group-add '"${DOCKER_SOCK_GID}"' -v '"${DOCKER_SOCK_PATH}"':/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DOCKER_HOST=unix:///var/run/docker.sock -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_POLICY_ALLOW_CREATE_PULL_REQUEST -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e RUNNER_TEMP -e GITHUB_AW_OTEL_TRACE_ID -e GITHUB_AW_OTEL_PARENT_SPAN_ID -e OTEL_EXPORTER_OTLP_HEADERS -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw -v '"${RUNNER_TEMP}"'/gh-aw/safeoutputs:'"${RUNNER_TEMP}"'/gh-aw/safeoutputs:rw ghcr.io/github/gh-aw-mcpg:v0.3.30' - # Generate JSON config for MCP gateway + mkdir -p "$HOME/.copilot" GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node) - cat << GH_AW_MCP_CONFIG_8cb9074ab13d6252_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" + cat << GH_AW_MCP_CONFIG_40aaf988fe3a4dfe_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" { "mcpServers": { "safeoutputs": { + "type": "stdio", "container": "ghcr.io/github/gh-aw-node", "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw", "${RUNNER_TEMP}/gh-aw/safeoutputs:${RUNNER_TEMP}/gh-aw/safeoutputs:rw", "/tmp/gh-aw:/tmp/gh-aw:rw"], "args": ["-w", "\${GITHUB_WORKSPACE}"], @@ -1132,31 +1102,34 @@ jobs: } }, "serena": { - "type": "stdio", - "container": "ghcr.io/github/serena-mcp-server:latest", - "entrypoint": "serena", - "entrypointArgs": [ - "start-mcp-server", - "--context", - "codex", - "--project", - "\${GITHUB_WORKSPACE}" - ], - "mounts": [ - "\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw" - ], - "args": [ - "--network", - "host" - ], - "guard-policies": { - "write-sink": { - "accept": [ - "*" - ] + "type": "stdio", + "container": "ghcr.io/github/serena-mcp-server:latest", + "entrypoint": "serena", + "entrypointArgs": [ + "start-mcp-server", + "--context", + "codex", + "--project", + "\${GITHUB_WORKSPACE}" + ], + "mounts": [ + "\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw" + ], + "args": [ + "--network", + "host" + ], + "tools": [ + "*" + ], + "guard-policies": { + "write-sink": { + "accept": [ + "*" + ] + } } } - } }, "gateway": { "port": $MCP_GATEWAY_PORT, @@ -1170,34 +1143,7 @@ jobs: } } } - GH_AW_MCP_CONFIG_8cb9074ab13d6252_EOF - - # Sync converter output to writable CODEX_HOME for Codex - mkdir -p /tmp/gh-aw/mcp-config - cat > "/tmp/gh-aw/mcp-config/config.toml" << GH_AW_CODEX_SHELL_POLICY_d8326fb8068eb0d4_EOF - - model_provider = "openai-proxy" - - [model_providers.openai-proxy] - name = "OpenAI AWF proxy" - base_url = "http://172.30.0.30:10000" - env_key = "OPENAI_API_KEY" - supports_websockets = false - [shell_environment_policy] - inherit = "core" - include_only = ["^CODEX_API_KEY$", "^GH_AW_ASSETS_ALLOWED_EXTS$", "^GH_AW_ASSETS_BRANCH$", "^GH_AW_ASSETS_MAX_SIZE_KB$", "^GH_AW_SAFE_OUTPUTS$", "^GITHUB_REPOSITORY$", "^GITHUB_SERVER_URL$", "^HOME$", "^OPENAI_API_KEY$", "^PATH$"] - GH_AW_CODEX_SHELL_POLICY_d8326fb8068eb0d4_EOF - awk ' - BEGIN { skip_openai_proxy = 0 } - /^[[:space:]]*model_provider[[:space:]]*=/ { next } - /^\[model_providers\.openai-proxy\][[:space:]]*$/ { skip_openai_proxy = 1; next } - /^\[/ { skip_openai_proxy = 0 } - !skip_openai_proxy { print } - ' "${RUNNER_TEMP}/gh-aw/mcp-config/config.toml" >> "/tmp/gh-aw/mcp-config/config.toml" - chmod 600 "/tmp/gh-aw/mcp-config/config.toml" - mkdir -p "${CODEX_HOME}" - if [ "/tmp/gh-aw/mcp-config/config.toml" != "${CODEX_HOME}/config.toml" ]; then cp "/tmp/gh-aw/mcp-config/config.toml" "${CODEX_HOME}/config.toml"; fi - chmod 600 "${CODEX_HOME}/config.toml" + GH_AW_MCP_CONFIG_40aaf988fe3a4dfe_EOF - name: Mount MCP servers as CLIs id: mount-mcp-clis continue-on-error: true @@ -1227,15 +1173,25 @@ jobs: CLI_PROXY_IMAGE: 'ghcr.io/github/gh-aw-mcpg:v0.3.30' run: | bash "${RUNNER_TEMP}/gh-aw/actions/start_cli_proxy.sh" - - name: Execute Codex CLI + - name: Execute GitHub Copilot CLI id: agentic_execution + # Copilot CLI tool arguments (sorted): + timeout-minutes: 15 run: | set -o pipefail printf '%s' "$(date +%s%3N)" > /tmp/gh-aw/agent_cli_start_ms.txt - mkdir -p "$CODEX_HOME/logs" && touch /tmp/gh-aw/agent-step-summary.md + trap 'rm -f "$HOME/.copilot/settings.json"' EXIT + mkdir -p "$HOME/.copilot" + printf '%s' '{"builtInAgents":{"rubberDuck":false},"lspServers":{"typescript":{"command":"typescript-language-server","args":["--stdio"],"fileExtensions":{".cjs":"javascript",".js":"javascript",".mjs":"javascript",".ts":"typescript",".tsx":"typescriptreact"}}}}' > "$HOME/.copilot/settings.json" + export XDG_CONFIG_HOME="$HOME" + export GH_AW_MCP_CONFIG="$HOME/.copilot/mcp-config.json" + touch /tmp/gh-aw/agent-step-summary.md + GH_AW_NODE_BIN=$(command -v node 2>/dev/null || true) + export GH_AW_NODE_BIN + export COPILOT_API_KEY="$COPILOT_DUMMY_BYOK" (umask 177 && touch /tmp/gh-aw/agent-stdio.log) - GH_AW_MAX_AI_CREDITS="${{ vars.GH_AW_DEFAULT_MAX_AI_CREDITS || '1000' }}" - printf '%s\n' "{\"\$schema\":\"https://github.com/github/gh-aw-firewall/releases/download/v0.27.11/awf-config.schema.json\",\"network\":{\"allowDomains\":[\"*.githubusercontent.com\",\"*.grafana.net\",\"*.sentry.io\",\"172.30.0.1\",\"api.github.com\",\"api.openai.com\",\"api.snapcraft.io\",\"archive.ubuntu.com\",\"azure.archive.ubuntu.com\",\"cdn.playwright.dev\",\"chatgpt.com\",\"codeload.github.com\",\"crl.geotrust.com\",\"crl.globalsign.com\",\"crl.identrust.com\",\"crl.sectigo.com\",\"crl.thawte.com\",\"crl.usertrust.com\",\"crl.verisign.com\",\"crl3.digicert.com\",\"crl4.digicert.com\",\"crls.ssl.com\",\"docs.github.com\",\"github-cloud.githubusercontent.com\",\"github-cloud.s3.amazonaws.com\",\"github.blog\",\"github.com\",\"github.githubassets.com\",\"go.dev\",\"golang.org\",\"goproxy.io\",\"host.docker.internal\",\"json-schema.org\",\"json.schemastore.org\",\"keyserver.ubuntu.com\",\"lfs.github.com\",\"objects.githubusercontent.com\",\"ocsp.digicert.com\",\"ocsp.geotrust.com\",\"ocsp.globalsign.com\",\"ocsp.identrust.com\",\"ocsp.sectigo.com\",\"ocsp.ssl.com\",\"ocsp.thawte.com\",\"ocsp.usertrust.com\",\"ocsp.verisign.com\",\"openai.com\",\"packagecloud.io\",\"packages.cloud.google.com\",\"packages.microsoft.com\",\"patch-diff.githubusercontent.com\",\"pkg.go.dev\",\"playwright.download.prss.microsoft.com\",\"ppa.launchpad.net\",\"proxy.golang.org\",\"raw.githubusercontent.com\",\"s.symcb.com\",\"s.symcd.com\",\"security.ubuntu.com\",\"storage.googleapis.com\",\"sum.golang.org\",\"ts-crl.ws.symantec.com\",\"ts-ocsp.ws.symantec.com\",\"www.googleapis.com\"]},\"apiProxy\":{\"enabled\":true,\"enableTokenSteering\":true,\"maxRuns\":500,\"maxAiCredits\":${GH_AW_MAX_AI_CREDITS},\"maxCacheMisses\":5,\"models\":{\"agent\":[\"sonnet-6x\",\"gpt-5.5\",\"gpt-5.4\",\"gpt-5.3\",\"gemini-pro\",\"any\"],\"antigravity\":[\"copilot/antigravity*\",\"google/antigravity*\",\"gemini/antigravity*\"],\"any\":[\"copilot/*\",\"anthropic/*\",\"openai/*\",\"google/*\",\"gemini/*\"],\"claude\":[\"agent\"],\"codex\":[\"agent\"],\"coding\":[\"copilot/gpt-5*codex*\",\"openai/gpt-5*codex*\",\"gpt-5-codex\"],\"computer-use\":[\"copilot/*computer-use*\",\"google/*computer-use*\",\"gemini/*computer-use*\",\"openai/*computer-use*\"],\"copilot\":[\"agent\"],\"deep-research\":[\"copilot/deep-research*\",\"copilot/o3-deep-research*\",\"copilot/o4-mini-deep-research*\",\"google/deep-research*\",\"gemini/deep-research*\",\"openai/o3-deep-research*\",\"openai/o4-mini-deep-research*\"],\"gemini\":[\"agent\"],\"gemini-3-flash\":[\"copilot/gemini-3*flash*\",\"google/gemini-3*flash*\",\"gemini/gemini-3*flash*\"],\"gemini-3-pro\":[\"copilot/gemini-3*pro*\",\"google/gemini-3*pro*\",\"google/nano-banana*\",\"gemini/gemini-3*pro*\"],\"gemini-3.1-flash\":[\"copilot/gemini-3.1*flash*\",\"google/gemini-3.1*flash*\",\"gemini/gemini-3.1*flash*\"],\"gemini-3.1-pro\":[\"copilot/gemini-3.1*pro*\",\"google/gemini-3.1*pro*\",\"gemini/gemini-3.1*pro*\"],\"gemini-3.5-flash\":[\"copilot/gemini-3.5*flash*\",\"google/gemini-3.5*flash*\",\"gemini/gemini-3.5*flash*\"],\"gemini-flash\":[\"copilot/gemini-*flash*\",\"google/gemini-*flash*\",\"gemini/gemini-*flash*\"],\"gemini-flash-lite\":[\"copilot/gemini-*flash*lite*\",\"google/gemini-*flash*lite*\",\"gemini/gemini-*flash*lite*\"],\"gemini-pro\":[\"copilot/gemini-*pro*\",\"google/gemini-*pro*\",\"gemini/gemini-*pro*\"],\"gemma\":[\"copilot/gemma*\",\"google/gemma*\",\"gemini/gemma*\"],\"gpt-5\":[\"copilot/gpt-5*\",\"openai/gpt-5*\"],\"gpt-5-codex\":[\"copilot/gpt-5*codex*\",\"openai/gpt-5*codex*\"],\"gpt-5-mini\":[\"copilot/gpt-5*mini*\",\"openai/gpt-5*mini*\"],\"gpt-5-nano\":[\"copilot/gpt-5*nano*\",\"openai/gpt-5*nano*\"],\"gpt-5-pro\":[\"copilot/gpt-5*pro*\",\"openai/gpt-5*pro*\"],\"gpt-5.1\":[\"copilot/gpt-5.1*\",\"openai/gpt-5.1*\"],\"gpt-5.2\":[\"copilot/gpt-5.2*\",\"openai/gpt-5.2*\"],\"gpt-5.3\":[\"copilot/gpt-5.3*\",\"openai/gpt-5.3*\"],\"gpt-5.4\":[\"copilot/gpt-5.4*\",\"openai/gpt-5.4*\"],\"gpt-5.5\":[\"copilot/gpt-5.5*\",\"openai/gpt-5.5*\"],\"haiku\":[\"copilot/*haiku*\",\"anthropic/*haiku*\"],\"image-generation\":[\"copilot/gpt-image*\",\"openai/gpt-image*\",\"openai/chatgpt-image*\",\"copilot/gemini-*image*\",\"google/gemini-*image*\",\"gemini/gemini-*image*\",\"google/imagen*\"],\"large\":[\"sonnet\",\"gpt-5-pro\",\"gpt-5\",\"gemini-pro\"],\"mai-code\":[\"copilot/MAI-Code*\",\"copilot/mai-code*\",\"openai/MAI-Code*\"],\"mini\":[\"haiku\",\"gpt-5-mini\",\"gpt-5-nano\",\"gemini-flash-lite\"],\"nano-banana\":[\"copilot/nano-banana*\",\"google/nano-banana*\",\"gemini/nano-banana*\"],\"opus\":[\"copilot/*opus*\",\"anthropic/*opus*\"],\"opusplan\":[\"opus?effort=high\"],\"reasoning\":[\"copilot/o1*\",\"copilot/o3*\",\"copilot/o4*\",\"openai/o1*\",\"openai/o3*\",\"openai/o4*\"],\"robotics\":[\"copilot/*robotics*\",\"google/*robotics*\",\"gemini/*robotics*\"],\"small\":[\"mini\"],\"small-agent\":[\"haiku\",\"gpt-5-mini\",\"gemini-flash\"],\"sonnet\":[\"copilot/*sonnet*\",\"anthropic/*sonnet*\"],\"sonnet-6x\":[\"copilot/*sonnet-4.5*\",\"copilot/*sonnet-4.6*\",\"copilot/*sonnet-4-5-*\",\"anthropic/*sonnet-4-5-*\",\"copilot/*sonnet-4-6*\",\"anthropic/*sonnet-4-6*\"],\"summarization\":[\"haiku\",\"gpt-5-mini\",\"gemini-flash-lite\",\"mini\"],\"vision\":[\"copilot/gemini-*image*\",\"google/gemini-*image*\",\"gemini/gemini-*image*\",\"copilot/gemini-*flash*\",\"google/gemini-*flash*\",\"gemini/gemini-*flash*\"]}},\"container\":{\"imageTag\":\"0.27.11,squid=sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d,agent=sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7,api-proxy=sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d\"}}" > "${RUNNER_TEMP}/gh-aw/awf-config.json" + GH_AW_MAX_AI_CREDITS="${GH_AW_MAX_AI_CREDITS:-1000}" + printf '%s\n' "{\"\$schema\":\"https://github.com/github/gh-aw-firewall/releases/download/v0.27.11/awf-config.schema.json\",\"network\":{\"allowDomains\":[\"*.githubusercontent.com\",\"*.grafana.net\",\"*.sentry.io\",\"api.business.githubcopilot.com\",\"api.enterprise.githubcopilot.com\",\"api.github.com\",\"api.githubcopilot.com\",\"api.individual.githubcopilot.com\",\"api.npms.io\",\"api.snapcraft.io\",\"archive.ubuntu.com\",\"azure.archive.ubuntu.com\",\"bun.sh\",\"cdn.jsdelivr.net\",\"cdn.playwright.dev\",\"codeload.github.com\",\"crl.geotrust.com\",\"crl.globalsign.com\",\"crl.identrust.com\",\"crl.sectigo.com\",\"crl.thawte.com\",\"crl.usertrust.com\",\"crl.verisign.com\",\"crl3.digicert.com\",\"crl4.digicert.com\",\"crls.ssl.com\",\"deb.nodesource.com\",\"deno.land\",\"docs.github.com\",\"esm.sh\",\"get.pnpm.io\",\"github-cloud.githubusercontent.com\",\"github-cloud.s3.amazonaws.com\",\"github.blog\",\"github.com\",\"github.githubassets.com\",\"go.dev\",\"golang.org\",\"googleapis.deno.dev\",\"googlechromelabs.github.io\",\"goproxy.io\",\"host.docker.internal\",\"json-schema.org\",\"json.schemastore.org\",\"jsr.io\",\"keyserver.ubuntu.com\",\"lfs.github.com\",\"nodejs.org\",\"npm.pkg.github.com\",\"npmjs.com\",\"npmjs.org\",\"objects.githubusercontent.com\",\"ocsp.digicert.com\",\"ocsp.geotrust.com\",\"ocsp.globalsign.com\",\"ocsp.identrust.com\",\"ocsp.sectigo.com\",\"ocsp.ssl.com\",\"ocsp.thawte.com\",\"ocsp.usertrust.com\",\"ocsp.verisign.com\",\"packagecloud.io\",\"packages.cloud.google.com\",\"packages.microsoft.com\",\"patch-diff.githubusercontent.com\",\"pkg.go.dev\",\"playwright.download.prss.microsoft.com\",\"ppa.launchpad.net\",\"proxy.golang.org\",\"raw.githubusercontent.com\",\"registry.bower.io\",\"registry.npmjs.com\",\"registry.npmjs.org\",\"registry.yarnpkg.com\",\"repo.yarnpkg.com\",\"s.symcb.com\",\"s.symcd.com\",\"security.ubuntu.com\",\"skimdb.npmjs.com\",\"storage.googleapis.com\",\"sum.golang.org\",\"telemetry.enterprise.githubcopilot.com\",\"telemetry.vercel.com\",\"ts-crl.ws.symantec.com\",\"ts-ocsp.ws.symantec.com\",\"www.googleapis.com\",\"www.npmjs.com\",\"www.npmjs.org\",\"yarnpkg.com\"]},\"apiProxy\":{\"enabled\":true,\"enableTokenSteering\":true,\"maxRuns\":500,\"maxAiCredits\":${GH_AW_MAX_AI_CREDITS},\"maxCacheMisses\":5,\"models\":{\"agent\":[\"sonnet-6x\",\"gpt-5.5\",\"gpt-5.4\",\"gpt-5.3\",\"gemini-pro\",\"any\"],\"antigravity\":[\"copilot/antigravity*\",\"google/antigravity*\",\"gemini/antigravity*\"],\"any\":[\"copilot/*\",\"anthropic/*\",\"openai/*\",\"google/*\",\"gemini/*\"],\"claude\":[\"agent\"],\"codex\":[\"agent\"],\"coding\":[\"copilot/gpt-5*codex*\",\"openai/gpt-5*codex*\",\"gpt-5-codex\"],\"computer-use\":[\"copilot/*computer-use*\",\"google/*computer-use*\",\"gemini/*computer-use*\",\"openai/*computer-use*\"],\"copilot\":[\"agent\"],\"deep-research\":[\"copilot/deep-research*\",\"copilot/o3-deep-research*\",\"copilot/o4-mini-deep-research*\",\"google/deep-research*\",\"gemini/deep-research*\",\"openai/o3-deep-research*\",\"openai/o4-mini-deep-research*\"],\"gemini\":[\"agent\"],\"gemini-3-flash\":[\"copilot/gemini-3*flash*\",\"google/gemini-3*flash*\",\"gemini/gemini-3*flash*\"],\"gemini-3-pro\":[\"copilot/gemini-3*pro*\",\"google/gemini-3*pro*\",\"google/nano-banana*\",\"gemini/gemini-3*pro*\"],\"gemini-3.1-flash\":[\"copilot/gemini-3.1*flash*\",\"google/gemini-3.1*flash*\",\"gemini/gemini-3.1*flash*\"],\"gemini-3.1-pro\":[\"copilot/gemini-3.1*pro*\",\"google/gemini-3.1*pro*\",\"gemini/gemini-3.1*pro*\"],\"gemini-3.5-flash\":[\"copilot/gemini-3.5*flash*\",\"google/gemini-3.5*flash*\",\"gemini/gemini-3.5*flash*\"],\"gemini-flash\":[\"copilot/gemini-*flash*\",\"google/gemini-*flash*\",\"gemini/gemini-*flash*\"],\"gemini-flash-lite\":[\"copilot/gemini-*flash*lite*\",\"google/gemini-*flash*lite*\",\"gemini/gemini-*flash*lite*\"],\"gemini-pro\":[\"copilot/gemini-*pro*\",\"google/gemini-*pro*\",\"gemini/gemini-*pro*\"],\"gemma\":[\"copilot/gemma*\",\"google/gemma*\",\"gemini/gemma*\"],\"gpt-5\":[\"copilot/gpt-5*\",\"openai/gpt-5*\"],\"gpt-5-codex\":[\"copilot/gpt-5*codex*\",\"openai/gpt-5*codex*\"],\"gpt-5-mini\":[\"copilot/gpt-5*mini*\",\"openai/gpt-5*mini*\"],\"gpt-5-nano\":[\"copilot/gpt-5*nano*\",\"openai/gpt-5*nano*\"],\"gpt-5-pro\":[\"copilot/gpt-5*pro*\",\"openai/gpt-5*pro*\"],\"gpt-5.1\":[\"copilot/gpt-5.1*\",\"openai/gpt-5.1*\"],\"gpt-5.2\":[\"copilot/gpt-5.2*\",\"openai/gpt-5.2*\"],\"gpt-5.3\":[\"copilot/gpt-5.3*\",\"openai/gpt-5.3*\"],\"gpt-5.4\":[\"copilot/gpt-5.4*\",\"openai/gpt-5.4*\"],\"gpt-5.5\":[\"copilot/gpt-5.5*\",\"openai/gpt-5.5*\"],\"haiku\":[\"copilot/*haiku*\",\"anthropic/*haiku*\"],\"image-generation\":[\"copilot/gpt-image*\",\"openai/gpt-image*\",\"openai/chatgpt-image*\",\"copilot/gemini-*image*\",\"google/gemini-*image*\",\"gemini/gemini-*image*\",\"google/imagen*\"],\"large\":[\"sonnet\",\"gpt-5-pro\",\"gpt-5\",\"gemini-pro\"],\"mai-code\":[\"copilot/MAI-Code*\",\"copilot/mai-code*\",\"openai/MAI-Code*\"],\"mini\":[\"haiku\",\"gpt-5-mini\",\"gpt-5-nano\",\"gemini-flash-lite\"],\"nano-banana\":[\"copilot/nano-banana*\",\"google/nano-banana*\",\"gemini/nano-banana*\"],\"opus\":[\"copilot/*opus*\",\"anthropic/*opus*\"],\"opusplan\":[\"opus?effort=high\"],\"reasoning\":[\"copilot/o1*\",\"copilot/o3*\",\"copilot/o4*\",\"openai/o1*\",\"openai/o3*\",\"openai/o4*\"],\"robotics\":[\"copilot/*robotics*\",\"google/*robotics*\",\"gemini/*robotics*\"],\"small\":[\"mini\"],\"small-agent\":[\"haiku\",\"gpt-5-mini\",\"gemini-flash\"],\"sonnet\":[\"copilot/*sonnet*\",\"anthropic/*sonnet*\"],\"sonnet-6x\":[\"copilot/*sonnet-4.5*\",\"copilot/*sonnet-4.6*\",\"copilot/*sonnet-4-5-*\",\"anthropic/*sonnet-4-5-*\",\"copilot/*sonnet-4-6*\",\"anthropic/*sonnet-4-6*\"],\"summarization\":[\"haiku\",\"gpt-5-mini\",\"gemini-flash-lite\",\"mini\"],\"vision\":[\"copilot/gemini-*image*\",\"google/gemini-*image*\",\"gemini/gemini-*image*\",\"copilot/gemini-*flash*\",\"google/gemini-*flash*\",\"gemini/gemini-*flash*\"]}},\"container\":{\"imageTag\":\"0.27.11,squid=sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d,agent=sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7,api-proxy=sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d\"}}" > "${RUNNER_TEMP}/gh-aw/awf-config.json" cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json export GH_AW_MODELS_JSON_PATH="/tmp/gh-aw/models.json" GH_AW_DOCKER_HOST="" @@ -1255,28 +1211,41 @@ jobs: fi fi # shellcheck disable=SC1003,SC2086 - sudo -E awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" ${GH_AW_TOOL_CACHE_MOUNT:+--mount "$GH_AW_TOOL_CACHE_MOUNT"} ${GH_AW_DOCKER_HOST:+--docker-host "$GH_AW_DOCKER_HOST"} ${GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS} --env-all --exclude-env CODEX_API_KEY --exclude-env GH_TOKEN --exclude-env GITHUB_MCP_SERVER_TOKEN --exclude-env MCP_GATEWAY_API_KEY --exclude-env OPENAI_API_KEY --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --allow-host-ports 80,443,8080 --skip-pull --difc-proxy-host host.docker.internal:18443 --difc-proxy-ca-cert /tmp/gh-aw/difc-proxy-tls/ca.crt \ - -- /bin/bash -c 'set +o histexpand; export PATH="${RUNNER_TEMP}/gh-aw/mcp-cli/bin:$PATH" && : "${RUNNER_TOOL_CACHE:?RUNNER_TOOL_CACHE must be set}"; GH_AW_TOOL_CACHE="$RUNNER_TOOL_CACHE"; export PATH="$(find "$GH_AW_TOOL_CACHE" -maxdepth 5 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || true)"; fi; if [ -z "$GH_AW_NODE_EXEC" ]; then echo "node runtime missing on this runner — check runtimes.node in workflow YAML" >&2; exit 127; fi; GH_AW_NPM_GLOBAL_ROOT="$(npm root -g 2>/dev/null || true)"; if [ -n "$GH_AW_NPM_GLOBAL_ROOT" ]; then export NODE_PATH="${GH_AW_NPM_GLOBAL_ROOT}${NODE_PATH:+:${NODE_PATH}}"; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/codex_harness.cjs codex exec${GH_AW_MODEL_AGENT_CODEX:+ --model "$GH_AW_MODEL_AGENT_CODEX"} -c web_search="disabled" --dangerously-bypass-approvals-and-sandbox --skip-git-repo-check --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log - env: - CODEX_API_KEY: ${{ secrets.CODEX_API_KEY || secrets.OPENAI_API_KEY }} - CODEX_HOME: /tmp/gh-aw/mcp-config + sudo -E awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" ${GH_AW_TOOL_CACHE_MOUNT:+--mount "$GH_AW_TOOL_CACHE_MOUNT"} ${GH_AW_DOCKER_HOST:+--docker-host "$GH_AW_DOCKER_HOST"} ${GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS} --env-all --exclude-env COPILOT_GITHUB_TOKEN --exclude-env GH_TOKEN --exclude-env GITHUB_MCP_SERVER_TOKEN --exclude-env MCP_GATEWAY_API_KEY --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --allow-host-ports 80,443,8080 --skip-pull --difc-proxy-host host.docker.internal:18443 --difc-proxy-ca-cert /tmp/gh-aw/difc-proxy-tls/ca.crt \ + -- /bin/bash -c 'set +o histexpand; export PATH="${RUNNER_TEMP}/gh-aw/mcp-cli/bin:$PATH" && : "${RUNNER_TOOL_CACHE:?RUNNER_TOOL_CACHE must be set}"; GH_AW_TOOL_CACHE="$RUNNER_TOOL_CACHE"; export PATH="$(find "$GH_AW_TOOL_CACHE" -maxdepth 5 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || true)"; fi; if [ -z "$GH_AW_NODE_EXEC" ]; then echo "node runtime missing on this runner — check runtimes.node in workflow YAML" >&2; exit 127; fi; GH_AW_WORKSPACE_NODE_MODULES="${GITHUB_WORKSPACE:-$PWD}/node_modules"; if [ -d "$GH_AW_WORKSPACE_NODE_MODULES" ]; then export NODE_PATH="${GH_AW_WORKSPACE_NODE_MODULES}${NODE_PATH:+:${NODE_PATH}}"; fi; GH_AW_NPM_GLOBAL_ROOT="$(npm root -g 2>/dev/null || true)"; if [ -n "$GH_AW_NPM_GLOBAL_ROOT" ]; then export NODE_PATH="${GH_AW_NPM_GLOBAL_ROOT}${NODE_PATH:+:${NODE_PATH}}"; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/copilot_harness.cjs "$GH_AW_NODE_EXEC" "${RUNNER_TEMP}/gh-aw/actions/copilot_sdk_driver.cjs" /usr/local/bin/copilot' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log + env: + AWF_REFLECT_ENABLED: 1 + COPILOT_AGENT_RUNNER_TYPE: STANDALONE + COPILOT_DUMMY_BYOK: dummy-byok-key-for-offline-mode + COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} + COPILOT_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || vars.GH_AW_DEFAULT_MODEL_COPILOT || 'claude-sonnet-4.6' }} + COPILOT_SDK_URI: http://127.0.0.1:3002 + GH_AW_COPILOT_SDK_DRIVER: 1 + GH_AW_COPILOT_SDK_SERVER_ARGS: '["--headless","--no-auto-update","--port","3002","--add-dir","/tmp/gh-aw/","--log-level","all","--log-dir","/tmp/gh-aw/sandbox/agent/logs/","--disable-builtin-mcps","--no-ask-user","--allow-all-tools","--add-dir","/tmp/gh-aw/cache-memory/","--allow-all-paths"]' + GH_AW_LLM_PROVIDER: github + GH_AW_MAX_AI_CREDITS: ${{ vars.GH_AW_DEFAULT_MAX_AI_CREDITS || '1000' }} + GH_AW_MAX_TOOL_DENIALS: 5 GH_AW_MAX_TURNS: ${{ vars.GH_AW_DEFAULT_MAX_TURNS || '' }} - GH_AW_MCP_CONFIG: ${{ runner.temp }}/gh-aw/mcp-config/config.toml - GH_AW_MODEL_AGENT_CODEX: ${{ vars.GH_AW_MODEL_AGENT_CODEX || vars.GH_AW_DEFAULT_MODEL_CODEX || 'gpt-5.4' }} GH_AW_PHASE: agent GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} + GH_AW_TIMEOUT_MINUTES: 15 GH_AW_VERSION: dev GH_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || github.token }} + GITHUB_API_URL: ${{ github.api_url }} GITHUB_AW: true + GITHUB_COPILOT_INTEGRATION_ID: agentic-workflows + GITHUB_HEAD_REF: ${{ github.head_ref }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + GITHUB_REF_NAME: ${{ github.ref_name }} + GITHUB_SERVER_URL: ${{ github.server_url }} GITHUB_STEP_SUMMARY: /tmp/gh-aw/agent-step-summary.md + GITHUB_WORKSPACE: ${{ github.workspace }} GIT_AUTHOR_EMAIL: github-actions[bot]@users.noreply.github.com GIT_AUTHOR_NAME: github-actions[bot] GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com GIT_COMMITTER_NAME: github-actions[bot] - OPENAI_API_KEY: ${{ secrets.CODEX_API_KEY || secrets.OPENAI_API_KEY }} RUNNER_TEMP: ${{ runner.temp }} - RUST_LOG: ${{ runner.debug == 1 && 'trace,hyper_util=info,mio=info,reqwest=info,os_info=info,codex_otel=warn,codex_core=debug,ocodex_exec=debug' || 'warn' }} TRACEPARENT: ${{ env.GITHUB_AW_OTEL_TRACE_ID != '' && env.GITHUB_AW_OTEL_PARENT_SPAN_ID != '' && format('00-{0}-{1}-01', env.GITHUB_AW_OTEL_TRACE_ID, env.GITHUB_AW_OTEL_PARENT_SPAN_ID) || '' }} - name: Stop CLI Proxy if: always() @@ -1293,6 +1262,10 @@ jobs: GITHUB_SERVER_URL: ${{ github.server_url }} GITHUB_TOKEN: ${{ github.token }} run: bash "${RUNNER_TEMP}/gh-aw/actions/configure_git_credentials.sh" + - name: Copy Copilot session state files to logs + if: always() + continue-on-error: true + run: bash "${RUNNER_TEMP}/gh-aw/actions/copy_copilot_session_state.sh" - name: Stop MCP Gateway if: always() continue-on-error: true @@ -1312,12 +1285,11 @@ jobs: const { main } = require('${{ runner.temp }}/gh-aw/actions/redact_secrets.cjs'); await main(); env: - GH_AW_SECRET_NAMES: 'CODEX_API_KEY,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN,OPENAI_API_KEY' - SECRET_CODEX_API_KEY: ${{ secrets.CODEX_API_KEY }} + GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN' + SECRET_COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} SECRET_GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }} SECRET_GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }} SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - SECRET_OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} - name: Append agent step summary if: always() run: bash "${RUNNER_TEMP}/gh-aw/actions/append_agent_step_summary.sh" @@ -1334,7 +1306,7 @@ jobs: uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} - GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,*.grafana.net,*.sentry.io,127.0.0.1,172.30.0.1,::1,api.github.com,api.openai.com,api.snapcraft.io,app.renovatebot.com,appveyor.com,archive.ubuntu.com,azure.archive.ubuntu.com,badgen.net,cdn.playwright.dev,chatgpt.com,circleci.com,codacy.com,codeclimate.com,codecov.io,codeload.github.com,coveralls.io,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,deepsource.io,docs.github.com,drone.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,go.dev,golang.org,goproxy.io,host.docker.internal,img.shields.io,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,localhost,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,openai.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,patch-diff.githubusercontent.com,pkg.go.dev,playwright.download.prss.microsoft.com,ppa.launchpad.net,proxy.golang.org,raw.githubusercontent.com,readthedocs.io,readthedocs.org,renovatebot.com,s.symcb.com,s.symcd.com,security.ubuntu.com,semaphoreci.com,shields.io,snyk.io,sonarcloud.io,sonarqube.com,storage.googleapis.com,sum.golang.org,travis-ci.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com" + GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,*.grafana.net,*.sentry.io,127.0.0.1,::1,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,app.renovatebot.com,appveyor.com,archive.ubuntu.com,azure.archive.ubuntu.com,badgen.net,bun.sh,cdn.jsdelivr.net,cdn.playwright.dev,circleci.com,codacy.com,codeclimate.com,codecov.io,codeload.github.com,coveralls.io,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,deb.nodesource.com,deepsource.io,deno.land,docs.github.com,drone.io,esm.sh,get.pnpm.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,go.dev,golang.org,googleapis.deno.dev,googlechromelabs.github.io,goproxy.io,host.docker.internal,img.shields.io,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,lfs.github.com,localhost,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,patch-diff.githubusercontent.com,pkg.go.dev,playwright.download.prss.microsoft.com,ppa.launchpad.net,proxy.golang.org,raw.githubusercontent.com,readthedocs.io,readthedocs.org,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,renovatebot.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,semaphoreci.com,shields.io,skimdb.npmjs.com,snyk.io,sonarcloud.io,sonarqube.com,storage.googleapis.com,sum.golang.org,telemetry.enterprise.githubcopilot.com,telemetry.vercel.com,travis-ci.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com,www.npmjs.com,www.npmjs.org,yarnpkg.com" GITHUB_SERVER_URL: ${{ github.server_url }} GITHUB_API_URL: ${{ github.api_url }} GH_AW_COMMANDS: "[\"smoke-codex\"]" @@ -1348,12 +1320,12 @@ jobs: if: always() uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: - GH_AW_AGENT_OUTPUT: /tmp/gh-aw/agent-stdio.log + GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/ with: script: | const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io, getOctokit); - const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_codex_log.cjs'); + const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_copilot_log.cjs'); await main(); - name: Parse MCP Gateway logs for step summary if: always() @@ -1441,7 +1413,7 @@ jobs: name: agent path: | /tmp/gh-aw/aw-prompts/prompt.txt - /tmp/gh-aw/mcp-config/logs/ + /tmp/gh-aw/sandbox/agent/logs/ /tmp/gh-aw/redacted-urls.log /tmp/gh-aw/mcp-logs/ /tmp/gh-aw/agent_usage.json @@ -1509,9 +1481,9 @@ jobs: env: GH_AW_SETUP_WORKFLOW_NAME: "Smoke Codex" GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/smoke-codex.lock.yml@${{ github.ref }} - GH_AW_INFO_VERSION: "0.142.1" + GH_AW_INFO_VERSION: "1.0.65" GH_AW_INFO_AWF_VERSION: "v0.27.11" - GH_AW_INFO_ENGINE_ID: "codex" + GH_AW_INFO_ENGINE_ID: "copilot" - name: Report TruffleHog secret scan failure if: always() && needs.trufflehog_scan.result == 'failure' && needs.trufflehog_scan.outputs.secrets_found == 'true' uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 @@ -1719,7 +1691,7 @@ jobs: GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }} GH_AW_WORKFLOW_ID: "smoke-codex" GH_AW_ACTION_FAILURE_ISSUE_EXPIRES_HOURS: "12" - GH_AW_ENGINE_ID: "codex" + GH_AW_ENGINE_ID: "copilot" GH_AW_SECRET_VERIFICATION_RESULT: ${{ needs.activation.outputs.secret_verification_result }} GH_AW_CHECKOUT_PR_SUCCESS: ${{ needs.agent.outputs.checkout_pr_success }} GH_AW_EFFECTIVE_TOKENS: ${{ needs.agent.outputs.effective_tokens || '' }} @@ -1732,7 +1704,7 @@ jobs: GH_AW_MCP_POLICY_ERROR: ${{ needs.agent.outputs.mcp_policy_error }} GH_AW_AGENTIC_ENGINE_TIMEOUT: ${{ needs.agent.outputs.agentic_engine_timeout }} GH_AW_MODEL_NOT_SUPPORTED_ERROR: ${{ needs.agent.outputs.model_not_supported_error }} - GH_AW_ENGINE_API_HOSTS: "api.openai.com" + GH_AW_ENGINE_API_HOSTS: "api.enterprise.githubcopilot.com,api.githubcopilot.com,api.business.githubcopilot.com,api.individual.githubcopilot.com" GH_AW_LOCKDOWN_CHECK_FAILED: ${{ needs.activation.outputs.lockdown_check_failed }} GH_AW_STALE_LOCK_FILE_FAILED: ${{ needs.activation.outputs.stale_lock_file_failed }} GH_AW_DAILY_AI_CREDITS_EXCEEDED: ${{ needs.activation.outputs.daily_ai_credits_exceeded }} @@ -1810,9 +1782,9 @@ jobs: env: GH_AW_SETUP_WORKFLOW_NAME: "Smoke Codex" GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/smoke-codex.lock.yml@${{ github.ref }} - GH_AW_INFO_VERSION: "0.142.1" + GH_AW_INFO_VERSION: "1.0.65" GH_AW_INFO_AWF_VERSION: "v0.27.11" - GH_AW_INFO_ENGINE_ID: "codex" + GH_AW_INFO_ENGINE_ID: "copilot" - name: Download agent output artifact id: download-agent-output continue-on-error: true @@ -1837,6 +1809,8 @@ jobs: run: | rm -rf /tmp/gh-aw/sandbox/firewall/logs rm -rf /tmp/gh-aw/sandbox/firewall/audit + - name: Download container images + run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.27.11@sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7 ghcr.io/github/gh-aw-firewall/api-proxy:0.27.11@sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d ghcr.io/github/gh-aw-firewall/squid:0.27.11@sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d - name: Check if detection needed id: detection_guard if: always() @@ -1898,102 +1872,32 @@ jobs: with: node-version: '24' package-manager-cache: false - - name: Install Codex CLI - run: npm install --ignore-scripts -g @openai/codex@0.142.1 + - name: Install GitHub Copilot CLI + run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" 1.0.65 + env: + GH_HOST: github.com - name: Install AWF binary run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.27.11 - - name: Download container images - run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.27.11@sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7 ghcr.io/github/gh-aw-firewall/api-proxy:0.27.11@sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d ghcr.io/github/gh-aw-firewall/squid:0.27.11@sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d ghcr.io/github/gh-aw-mcpg:v0.3.30@sha256:35625d1a2269b1238606078c879f59a91cffc4ac33eb54bf39c6418822c1a8be - - name: Start MCP Gateway - id: start-mcp-gateway - env: - CODEX_HOME: /tmp/gh-aw/mcp-config - run: | - set -eo pipefail - mkdir -p "${RUNNER_TEMP}/gh-aw/mcp-config" - - # Export gateway environment variables for MCP config and gateway script - export MCP_GATEWAY_PORT="8080" - export MCP_GATEWAY_DOMAIN="host.docker.internal" - export MCP_GATEWAY_HOST_DOMAIN="localhost" - MCP_GATEWAY_API_KEY=$(openssl rand -base64 45 | tr -d '/+=') - echo "::add-mask::${MCP_GATEWAY_API_KEY}" - export MCP_GATEWAY_API_KEY - export MCP_GATEWAY_PAYLOAD_DIR="/tmp/gh-aw/mcp-payloads" - mkdir -p "${MCP_GATEWAY_PAYLOAD_DIR}" - export MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD="524288" - export DEBUG="*" - - export GH_AW_ENGINE="codex" - MCP_GATEWAY_UID=$(id -u 2>/dev/null || echo '0') - MCP_GATEWAY_GID=$(id -g 2>/dev/null || echo '0') - case "${DOCKER_HOST:-}" in - unix://* ) DOCKER_SOCK_PATH="${DOCKER_HOST#unix://}" ;; - /* ) DOCKER_SOCK_PATH="$DOCKER_HOST" ;; - * ) DOCKER_SOCK_PATH=/var/run/docker.sock ;; - esac - DOCKER_SOCK_GID=$(stat -c '%g' "$DOCKER_SOCK_PATH" 2>/dev/null || echo '0') - export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host --name awmg-mcpg --add-host host.docker.internal:127.0.0.1 --user '"${MCP_GATEWAY_UID}"':'"${MCP_GATEWAY_GID}"' --group-add '"${DOCKER_SOCK_GID}"' -v '"${DOCKER_SOCK_PATH}"':/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DOCKER_HOST=unix:///var/run/docker.sock -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_POLICY_ALLOW_CREATE_PULL_REQUEST -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e RUNNER_TEMP -e CODEX_HOME -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw -v '"${RUNNER_TEMP}"'/gh-aw/safeoutputs:'"${RUNNER_TEMP}"'/gh-aw/safeoutputs:rw ghcr.io/github/gh-aw-mcpg:v0.3.30' - - cat > "${RUNNER_TEMP}/gh-aw/mcp-config/config.toml" << GH_AW_MCP_CONFIG_2f5d9885311152cf_EOF - [history] - persistence = "none" - - [shell_environment_policy] - inherit = "core" - include_only = ["^CODEX_API_KEY$", "^HOME$", "^OPENAI_API_KEY$", "^PATH$"] - GH_AW_MCP_CONFIG_2f5d9885311152cf_EOF - - # Generate JSON config for MCP gateway - GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node) - cat << GH_AW_MCP_CONFIG_b9b3d113c1a9458e_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" - { - "mcpServers": { - }, - "gateway": { - "port": $MCP_GATEWAY_PORT, - "domain": "${MCP_GATEWAY_DOMAIN}", - "apiKey": "${MCP_GATEWAY_API_KEY}", - "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}" - } - } - GH_AW_MCP_CONFIG_b9b3d113c1a9458e_EOF - - # Sync converter output to writable CODEX_HOME for Codex - mkdir -p /tmp/gh-aw/mcp-config - cat > "/tmp/gh-aw/mcp-config/config.toml" << GH_AW_CODEX_SHELL_POLICY_f48e0018706875a8_EOF - model_provider = "openai-proxy" - [model_providers.openai-proxy] - name = "OpenAI AWF proxy" - base_url = "http://172.30.0.30:10000" - env_key = "OPENAI_API_KEY" - supports_websockets = false - [shell_environment_policy] - inherit = "core" - include_only = ["^CODEX_API_KEY$", "^HOME$", "^OPENAI_API_KEY$", "^PATH$"] - GH_AW_CODEX_SHELL_POLICY_f48e0018706875a8_EOF - awk ' - BEGIN { skip_openai_proxy = 0 } - /^[[:space:]]*model_provider[[:space:]]*=/ { next } - /^\[model_providers\.openai-proxy\][[:space:]]*$/ { skip_openai_proxy = 1; next } - /^\[/ { skip_openai_proxy = 0 } - !skip_openai_proxy { print } - ' "${RUNNER_TEMP}/gh-aw/mcp-config/config.toml" >> "/tmp/gh-aw/mcp-config/config.toml" - chmod 600 "/tmp/gh-aw/mcp-config/config.toml" - mkdir -p "${CODEX_HOME}" - if [ "/tmp/gh-aw/mcp-config/config.toml" != "${CODEX_HOME}/config.toml" ]; then cp "/tmp/gh-aw/mcp-config/config.toml" "${CODEX_HOME}/config.toml"; fi - chmod 600 "${CODEX_HOME}/config.toml" - - name: Execute Codex CLI + - name: Execute GitHub Copilot CLI if: always() && steps.detection_guard.outputs.run_detection == 'true' continue-on-error: true id: detection_agentic_execution + # Copilot CLI tool arguments (sorted): + timeout-minutes: 20 run: | set -o pipefail printf '%s' "$(date +%s%3N)" > /tmp/gh-aw/agent_cli_start_ms.txt - mkdir -p "$CODEX_HOME/logs" && touch /tmp/gh-aw/agent-step-summary.md && mkdir -p /tmp/gh-aw/threat-detection && printf '%s' '{"type":"object","properties":{"prompt_injection":{"type":"boolean"},"secret_leak":{"type":"boolean"},"malicious_patch":{"type":"boolean"},"reasons":{"type":"array","items":{"type":"string"}}},"required":["prompt_injection","secret_leak","malicious_patch","reasons"],"additionalProperties":false}' > /tmp/gh-aw/threat-detection/detection_schema.json + trap 'rm -f "$HOME/.copilot/settings.json"' EXIT + mkdir -p "$HOME/.copilot" + printf '%s' '{"builtInAgents":{"rubberDuck":false}}' > "$HOME/.copilot/settings.json" + export XDG_CONFIG_HOME="$HOME" + touch /tmp/gh-aw/agent-step-summary.md + GH_AW_NODE_BIN=$(command -v node 2>/dev/null || true) + export GH_AW_NODE_BIN + export COPILOT_API_KEY="$COPILOT_DUMMY_BYOK" (umask 177 && touch /tmp/gh-aw/threat-detection/detection.log) - GH_AW_MAX_AI_CREDITS="${{ vars.GH_AW_DEFAULT_DETECTION_MAX_AI_CREDITS || '400' }}" - printf '%s\n' "{\"\$schema\":\"https://github.com/github/gh-aw-firewall/releases/download/v0.27.11/awf-config.schema.json\",\"network\":{\"allowDomains\":[\"172.30.0.1\",\"api.github.com\",\"api.openai.com\",\"chatgpt.com\",\"github.com\",\"host.docker.internal\",\"openai.com\"]},\"apiProxy\":{\"enabled\":true,\"enableTokenSteering\":true,\"maxRuns\":500,\"maxAiCredits\":${GH_AW_MAX_AI_CREDITS},\"maxCacheMisses\":5},\"container\":{\"imageTag\":\"0.27.11,squid=sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d,agent=sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7,api-proxy=sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d\"}}" > "${RUNNER_TEMP}/gh-aw/awf-config.json" + GH_AW_MAX_AI_CREDITS="${GH_AW_MAX_AI_CREDITS:-400}" + printf '%s\n' "{\"\$schema\":\"https://github.com/github/gh-aw-firewall/releases/download/v0.27.11/awf-config.schema.json\",\"network\":{\"allowDomains\":[\"api.business.githubcopilot.com\",\"api.enterprise.githubcopilot.com\",\"api.github.com\",\"api.githubcopilot.com\",\"api.individual.githubcopilot.com\",\"github.com\",\"host.docker.internal\",\"registry.npmjs.org\",\"telemetry.enterprise.githubcopilot.com\"]},\"apiProxy\":{\"enabled\":true,\"enableTokenSteering\":true,\"maxRuns\":500,\"maxAiCredits\":${GH_AW_MAX_AI_CREDITS},\"maxCacheMisses\":5},\"container\":{\"imageTag\":\"0.27.11,squid=sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d,agent=sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7,api-proxy=sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d\"}}" > "${RUNNER_TEMP}/gh-aw/awf-config.json" cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json export GH_AW_MODELS_JSON_PATH="/tmp/gh-aw/models.json" GH_AW_DOCKER_HOST="" @@ -2015,26 +1919,34 @@ jobs: fi fi # shellcheck disable=SC1003,SC2086 - sudo -E awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" ${GH_AW_TOOL_CACHE_MOUNT:+--mount "$GH_AW_TOOL_CACHE_MOUNT"} ${GH_AW_DOCKER_HOST:+--docker-host "$GH_AW_DOCKER_HOST"} ${GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS} --env-all --exclude-env CODEX_API_KEY --exclude-env OPENAI_API_KEY --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --allow-host-ports 80,443,8080 --skip-pull \ - -- /bin/bash -c 'set +o histexpand; : "${RUNNER_TOOL_CACHE:?RUNNER_TOOL_CACHE must be set}"; GH_AW_TOOL_CACHE="$RUNNER_TOOL_CACHE"; export PATH="$(find "$GH_AW_TOOL_CACHE" -maxdepth 5 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || true)"; fi; if [ -z "$GH_AW_NODE_EXEC" ]; then echo "node runtime missing on this runner — check runtimes.node in workflow YAML" >&2; exit 127; fi; GH_AW_NPM_GLOBAL_ROOT="$(npm root -g 2>/dev/null || true)"; if [ -n "$GH_AW_NPM_GLOBAL_ROOT" ]; then export NODE_PATH="${GH_AW_NPM_GLOBAL_ROOT}${NODE_PATH:+:${NODE_PATH}}"; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/codex_harness.cjs codex exec${GH_AW_MODEL_DETECTION_CODEX:+ --model "$GH_AW_MODEL_DETECTION_CODEX"} -c web_search="disabled" -c fetch="disabled" --dangerously-bypass-approvals-and-sandbox --skip-git-repo-check --output-schema /tmp/gh-aw/threat-detection/detection_schema.json -o /tmp/gh-aw/threat-detection/detection_result.json --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log - env: - CODEX_API_KEY: ${{ secrets.CODEX_API_KEY || secrets.OPENAI_API_KEY }} - CODEX_HOME: /tmp/gh-aw/mcp-config + sudo -E awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" ${GH_AW_TOOL_CACHE_MOUNT:+--mount "$GH_AW_TOOL_CACHE_MOUNT"} ${GH_AW_DOCKER_HOST:+--docker-host "$GH_AW_DOCKER_HOST"} ${GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS} --env-all --exclude-env COPILOT_GITHUB_TOKEN --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --allow-host-ports 80,443,8080 --skip-pull \ + -- /bin/bash -c 'set +o histexpand; : "${RUNNER_TOOL_CACHE:?RUNNER_TOOL_CACHE must be set}"; GH_AW_TOOL_CACHE="$RUNNER_TOOL_CACHE"; export PATH="$(find "$GH_AW_TOOL_CACHE" -maxdepth 5 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || true)"; fi; if [ -z "$GH_AW_NODE_EXEC" ]; then echo "node runtime missing on this runner — check runtimes.node in workflow YAML" >&2; exit 127; fi; GH_AW_NPM_GLOBAL_ROOT="$(npm root -g 2>/dev/null || true)"; if [ -n "$GH_AW_NPM_GLOBAL_ROOT" ]; then export NODE_PATH="${GH_AW_NPM_GLOBAL_ROOT}${NODE_PATH:+:${NODE_PATH}}"; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/copilot_harness.cjs /usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --no-ask-user --allow-all-tools --add-dir "${GITHUB_WORKSPACE}" --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log + env: + AWF_REFLECT_ENABLED: 1 + COPILOT_AGENT_RUNNER_TYPE: STANDALONE + COPILOT_DUMMY_BYOK: dummy-byok-key-for-offline-mode + COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} + COPILOT_MODEL: ${{ vars.GH_AW_MODEL_DETECTION_COPILOT || vars.GH_AW_DEFAULT_MODEL_COPILOT || 'claude-sonnet-4.6' }} + GH_AW_LLM_PROVIDER: github + GH_AW_MAX_AI_CREDITS: ${{ vars.GH_AW_DEFAULT_DETECTION_MAX_AI_CREDITS || '400' }} GH_AW_MAX_TURNS: ${{ vars.GH_AW_DEFAULT_MAX_TURNS || '' }} - GH_AW_MCP_CONFIG: ${{ runner.temp }}/gh-aw/mcp-config/config.toml - GH_AW_MODEL_DETECTION_CODEX: ${{ vars.GH_AW_MODEL_DETECTION_CODEX || vars.GH_AW_DEFAULT_MODEL_CODEX || 'gpt-5.4' }} GH_AW_PHASE: detection GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GH_AW_TIMEOUT_MINUTES: 20 GH_AW_VERSION: dev + GITHUB_API_URL: ${{ github.api_url }} GITHUB_AW: true + GITHUB_COPILOT_INTEGRATION_ID: agentic-workflows + GITHUB_HEAD_REF: ${{ github.head_ref }} + GITHUB_REF_NAME: ${{ github.ref_name }} + GITHUB_SERVER_URL: ${{ github.server_url }} GITHUB_STEP_SUMMARY: /tmp/gh-aw/agent-step-summary.md + GITHUB_WORKSPACE: ${{ github.workspace }} GIT_AUTHOR_EMAIL: github-actions[bot]@users.noreply.github.com GIT_AUTHOR_NAME: github-actions[bot] GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com GIT_COMMITTER_NAME: github-actions[bot] - OPENAI_API_KEY: ${{ secrets.CODEX_API_KEY || secrets.OPENAI_API_KEY }} RUNNER_TEMP: ${{ runner.temp }} - RUST_LOG: ${{ runner.debug == 1 && 'trace,hyper_util=info,mio=info,reqwest=info,os_info=info,codex_otel=warn,codex_core=debug,ocodex_exec=debug' || 'warn' }} TRACEPARENT: ${{ env.GITHUB_AW_OTEL_TRACE_ID != '' && env.GITHUB_AW_OTEL_PARENT_SPAN_ID != '' && format('00-{0}-{1}-01', env.GITHUB_AW_OTEL_TRACE_ID, env.GITHUB_AW_OTEL_PARENT_SPAN_ID) || '' }} - name: Parse threat detection token usage for step summary id: parse_detection_token_usage @@ -2121,9 +2033,9 @@ jobs: env: GH_AW_SETUP_WORKFLOW_NAME: "Smoke Codex" GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/smoke-codex.lock.yml@${{ github.ref }} - GH_AW_INFO_VERSION: "0.142.1" + GH_AW_INFO_VERSION: "1.0.65" GH_AW_INFO_AWF_VERSION: "v0.27.11" - GH_AW_INFO_ENGINE_ID: "codex" + GH_AW_INFO_ENGINE_ID: "copilot" - name: Check team membership for command workflow id: check_membership uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 @@ -2170,8 +2082,9 @@ jobs: GH_AW_DETECTION_CONCLUSION: ${{ needs.detection.outputs.detection_conclusion }} GH_AW_DETECTION_REASON: ${{ needs.detection.outputs.detection_reason }} GH_AW_EFFECTIVE_TOKENS: ${{ needs.agent.outputs.effective_tokens }} - GH_AW_ENGINE_ID: "codex" + GH_AW_ENGINE_ID: "copilot" GH_AW_ENGINE_MODEL: ${{ needs.agent.outputs.model }} + GH_AW_ENGINE_VERSION: "1.0.65" GH_AW_PROJECT_UTC: "-08:00" GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }} GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e 🔮 *The oracle has spoken through [{workflow_name}]({run_url})*{ai_credits_suffix}{history_link}\",\"runStarted\":\"🔮 The ancient spirits stir... [{workflow_name}]({run_url}) awakens to divine this {event_type}...\",\"runSuccess\":\"✨ The prophecy is fulfilled... [{workflow_name}]({run_url}) has completed its mystical journey. The stars align. 🌟\",\"runFailure\":\"🌑 The shadows whisper... [{workflow_name}]({run_url}) {status}. The oracle requires further meditation...\"}" @@ -2210,9 +2123,9 @@ jobs: env: GH_AW_SETUP_WORKFLOW_NAME: "Smoke Codex" GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/smoke-codex.lock.yml@${{ github.ref }} - GH_AW_INFO_VERSION: "0.142.1" + GH_AW_INFO_VERSION: "1.0.65" GH_AW_INFO_AWF_VERSION: "v0.27.11" - GH_AW_INFO_ENGINE_ID: "codex" + GH_AW_INFO_ENGINE_ID: "copilot" - name: Mask OTLP telemetry headers run: bash "${RUNNER_TEMP}/gh-aw/actions/mask_otlp_headers.sh" - name: Download agent output artifact @@ -2244,7 +2157,7 @@ jobs: env: GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} GH_AW_COMMENT_ID: ${{ needs.activation.outputs.comment_id }} - GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,*.grafana.net,*.sentry.io,127.0.0.1,172.30.0.1,::1,api.github.com,api.openai.com,api.snapcraft.io,app.renovatebot.com,appveyor.com,archive.ubuntu.com,azure.archive.ubuntu.com,badgen.net,cdn.playwright.dev,chatgpt.com,circleci.com,codacy.com,codeclimate.com,codecov.io,codeload.github.com,coveralls.io,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,deepsource.io,docs.github.com,drone.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,go.dev,golang.org,goproxy.io,host.docker.internal,img.shields.io,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,localhost,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,openai.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,patch-diff.githubusercontent.com,pkg.go.dev,playwright.download.prss.microsoft.com,ppa.launchpad.net,proxy.golang.org,raw.githubusercontent.com,readthedocs.io,readthedocs.org,renovatebot.com,s.symcb.com,s.symcd.com,security.ubuntu.com,semaphoreci.com,shields.io,snyk.io,sonarcloud.io,sonarqube.com,storage.googleapis.com,sum.golang.org,travis-ci.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com" + GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,*.grafana.net,*.sentry.io,127.0.0.1,::1,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,app.renovatebot.com,appveyor.com,archive.ubuntu.com,azure.archive.ubuntu.com,badgen.net,bun.sh,cdn.jsdelivr.net,cdn.playwright.dev,circleci.com,codacy.com,codeclimate.com,codecov.io,codeload.github.com,coveralls.io,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,deb.nodesource.com,deepsource.io,deno.land,docs.github.com,drone.io,esm.sh,get.pnpm.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,go.dev,golang.org,googleapis.deno.dev,googlechromelabs.github.io,goproxy.io,host.docker.internal,img.shields.io,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,lfs.github.com,localhost,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,patch-diff.githubusercontent.com,pkg.go.dev,playwright.download.prss.microsoft.com,ppa.launchpad.net,proxy.golang.org,raw.githubusercontent.com,readthedocs.io,readthedocs.org,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,renovatebot.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,semaphoreci.com,shields.io,skimdb.npmjs.com,snyk.io,sonarcloud.io,sonarqube.com,storage.googleapis.com,sum.golang.org,telemetry.enterprise.githubcopilot.com,telemetry.vercel.com,travis-ci.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com,www.npmjs.com,www.npmjs.org,yarnpkg.com" GITHUB_SERVER_URL: ${{ github.server_url }} GITHUB_API_URL: ${{ github.api_url }} GH_AW_SAFE_OUTPUT_ACTIONS: "{\"add_smoked_label\":\"add_smoked_label\"}" @@ -2454,9 +2367,9 @@ jobs: env: GH_AW_SETUP_WORKFLOW_NAME: "Smoke Codex" GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/smoke-codex.lock.yml@${{ github.ref }} - GH_AW_INFO_VERSION: "0.142.1" + GH_AW_INFO_VERSION: "1.0.65" GH_AW_INFO_AWF_VERSION: "v0.27.11" - GH_AW_INFO_ENGINE_ID: "codex" + GH_AW_INFO_ENGINE_ID: "copilot" - name: Download cache-memory artifact (default) id: download_cache_default uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 diff --git a/.github/workflows/smoke-codex.md b/.github/workflows/smoke-codex.md index 159e8ebb169..ea2ff750266 100644 --- a/.github/workflows/smoke-codex.md +++ b/.github/workflows/smoke-codex.md @@ -24,7 +24,9 @@ permissions: issues: read pull-requests: read name: Smoke Codex -engine: codex +engine: + id: copilot + copilot-sdk: true imports: - shared/gh.md - shared/reporting-otlp.md @@ -48,9 +50,21 @@ tools: bash: - "*" web-fetch: +lsp: + typescript: + command: typescript-language-server + args: ["--stdio"] + fileExtensions: + ".js": javascript + ".cjs": javascript + ".mjs": javascript + ".ts": typescript + ".tsx": typescriptreact runtimes: go: version: "1.26" + node: + version: "20" safe-outputs: allowed-domains: [default-safe-outputs] add-comment: @@ -124,6 +138,10 @@ features: - date field → `YYYY-MM-DD` - single-select field → an existing option name - If no editable issue fields are available, report this test as skipped with reason +11. **LSP TypeScript Testing**: Use the TypeScript language server (configured via `lsp.typescript` frontmatter) to count the number of functions in `${{ github.workspace }}/actions/setup/js/safe_output_helpers.cjs`: + - Open the file `${{ github.workspace }}/actions/setup/js/safe_output_helpers.cjs` via LSP + - Use LSP document symbols to list all symbols in the file and count functions + - Report the total function count as ✅ if at least 1 function is found, ❌ otherwise ## Output @@ -131,7 +149,7 @@ features: - Title: "Smoke Test: Codex - ${{ github.run_id }}" - Declare `temporary_id: aw_smoke_issue` on this `create_issue` output so that the `set_issue_field` message (test #10) can reference it via `issue_number: '#aw_smoke_issue'` - Body should include: - - Test results (✅ or ❌ for each test, including test #9 Cache Memory and test #10 Set Issue Field) + - Test results (✅ or ❌ for each test, including test #9 Cache Memory, test #10 Set Issue Field, and test #11 LSP TypeScript) - Overall status: PASS or FAIL - Run URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} - Timestamp From 079a3f8cd7a85d9375674ad3980bb6d9a9d82cb5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 27 Jun 2026 18:27:32 +0000 Subject: [PATCH 12/13] feat(lsp): pin packages to releases, add frontmatter version override - Add Version field to LSPServerConfig for per-language version override - Add NpmPackageVersions map to lspInstallSpec for default pinned npm versions - Add DefaultVersion to go/ruby specs for pinned non-npm installs - Update GenerateInstallSteps to emit versioned install commands - Strip leading v prefix from user-provided version to avoid double-v - Add version field to JSON schema - Update .github/aw/lsp.md docs with version column and override note - Add tests for default pinning, version override, and v-prefix handling - Recompile lock files to pick up new pinned versions Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .github/aw/lsp.md | 25 +++-- .github/workflows/jsweep.lock.yml | 2 +- .github/workflows/smoke-codex.lock.yml | 4 +- pkg/parser/schemas/main_workflow_schema.json | 5 + pkg/workflow/lsp_manager.go | 112 ++++++++++++++----- pkg/workflow/lsp_manager_test.go | 94 +++++++++++++++- 6 files changed, 198 insertions(+), 44 deletions(-) diff --git a/.github/aw/lsp.md b/.github/aw/lsp.md index 8898bbbf995..10e79ac0902 100644 --- a/.github/aw/lsp.md +++ b/.github/aw/lsp.md @@ -35,21 +35,24 @@ Each key under `lsp` is a language identifier (lowercase, alphanumeric, hyphens, | `command` | **yes** | Executable name or path for the language server | | `args` | no | Command-line arguments passed to the server on startup | | `fileExtensions` | **yes** | Map of file extension (with leading `.`) to LSP language ID | +| `version` | no | Package version to install (e.g. `"5.8.3"`). Overrides the built-in pinned default for known servers. | ## Built-in Servers -For the languages below, the compiler automatically injects an install step — no manual `steps:` entry is needed. +For the languages below, the compiler automatically injects an install step — no manual `steps:` entry is needed. Each server is pinned to a known-good release by default; use the `version` field to override. -| Language key | Install command | Example `command` | -|---|---|---| -| `bash` | `npm install -g --ignore-scripts bash-language-server` | `bash-language-server` | -| `go` | `go install golang.org/x/tools/gopls@latest` | `gopls` | -| `php` | `npm install -g --ignore-scripts intelephense` | `intelephense` | -| `python` | `npm install -g --ignore-scripts pyright` | `pyright-langserver` | -| `ruby` | `gem install solargraph` | `solargraph` | -| `rust` | `rustup component add rust-analyzer` | `rust-analyzer` | -| `typescript` | `npm install -g --ignore-scripts typescript typescript-language-server` | `typescript-language-server` | -| `yaml` | `npm install -g --ignore-scripts yaml-language-server` | `yaml-language-server` | +| Language key | Default version | Install command | Example `command` | +|---|---|---|---| +| `bash` | `5.4.0` | `npm install -g --ignore-scripts bash-language-server@5.4.0` | `bash-language-server` | +| `go` | `0.18.1` | `go install golang.org/x/tools/gopls@v0.18.1` | `gopls` | +| `php` | `1.14.1` | `npm install -g --ignore-scripts intelephense@1.14.1` | `intelephense` | +| `python` | `1.1.399` | `npm install -g --ignore-scripts pyright@1.1.399` | `pyright-langserver` | +| `ruby` | `0.50.0` | `gem install solargraph -v 0.50.0` | `solargraph` | +| `rust` | n/a | `rustup component add rust-analyzer` | `rust-analyzer` | +| `typescript` | `5.8.3` / `4.3.3` | `npm install -g --ignore-scripts typescript@5.8.3 typescript-language-server@4.3.3` | `typescript-language-server` | +| `yaml` | `1.15.0` | `npm install -g --ignore-scripts yaml-language-server@1.15.0` | `yaml-language-server` | + +> The `version` field overrides the pinned version for the primary language server package (the last package in the install list). For `typescript`, it controls `typescript-language-server`; `typescript` itself stays at its hardcoded companion version (`5.8.3`). Language keys not in this table still work — the compiler simply skips the auto-install step. Add a manual `steps:` entry to install the server yourself. diff --git a/.github/workflows/jsweep.lock.yml b/.github/workflows/jsweep.lock.yml index 19325502ca3..a07ec666d7b 100644 --- a/.github/workflows/jsweep.lock.yml +++ b/.github/workflows/jsweep.lock.yml @@ -535,7 +535,7 @@ jobs: - name: Install GitHub Copilot SDK (Node.js) run: cd "${GITHUB_WORKSPACE}" && npm install --ignore-scripts --no-save @github/copilot-sdk@1.0.4 - name: Install TypeScript LSP dependencies - run: npm install -g --ignore-scripts typescript typescript-language-server + run: npm install -g --ignore-scripts typescript@5.8.3 typescript-language-server@4.3.3 env: NPM_CONFIG_MIN_RELEASE_AGE: '3' timeout-minutes: 10 diff --git a/.github/workflows/smoke-codex.lock.yml b/.github/workflows/smoke-codex.lock.yml index b574c550584..ebd1e73b4a0 100644 --- a/.github/workflows/smoke-codex.lock.yml +++ b/.github/workflows/smoke-codex.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"f66218c1af651228b4ec3d19d0e98c5f28dd35a1030de8e39f23ec226b031511","body_hash":"ec2ee9864ce0d5f39c4d5c9c529fd3eeb4758bfdf8b426936f7e8126973fb1ab","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.65","copilot-sdk":"1.0.4"}} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"f66218c1af651228b4ec3d19d0e98c5f28dd35a1030de8e39f23ec226b031511","body_hash":"722513f82c6256103c0cf8abb58e23e79a10dfd972e4fa036a857a315ee30af1","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.65","copilot-sdk":"1.0.4"}} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_GRAFANA_AUTHORIZATION","GH_AW_OTEL_GRAFANA_ENDPOINT","GH_AW_OTEL_SENTRY_AUTHORIZATION","GH_AW_OTEL_SENTRY_ENDPOINT","GITHUB_TOKEN"],"actions":[{"repo":"actions-ecosystem/action-add-labels","sha":"c96b68fec76a0987cd93957189e9abd0b9a72ff1","version":"v1.1.3"},{"repo":"actions/cache/restore","sha":"27d5ce7f107fe9357f9df03efb73ab90386fccae","version":"v5.0.5"},{"repo":"actions/cache/save","sha":"27d5ce7f107fe9357f9df03efb73ab90386fccae","version":"v5.0.5"},{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7.0.0"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-go","sha":"4a3601121dd01d1626a1e23e37211e3254c1c06c","version":"v6.4.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.11","digest":"sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.11@sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.11","digest":"sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.11@sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d"},{"image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.11"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.11","digest":"sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.11@sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.30","digest":"sha256:35625d1a2269b1238606078c879f59a91cffc4ac33eb54bf39c6418822c1a8be","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.30@sha256:35625d1a2269b1238606078c879f59a91cffc4ac33eb54bf39c6418822c1a8be"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.4.0","digest":"sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036","pinned_image":"ghcr.io/github/github-mcp-server:v1.4.0@sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036"},{"image":"ghcr.io/github/serena-mcp-server:latest","digest":"sha256:bf343399e3725c45528f531a230f3a04521d4cdef29f9a5af6282ff0d3c393c5","pinned_image":"ghcr.io/github/serena-mcp-server:latest@sha256:bf343399e3725c45528f531a230f3a04521d4cdef29f9a5af6282ff0d3c393c5"}]} # This file was automatically generated by gh-aw. DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # @@ -638,7 +638,7 @@ jobs: - name: Install GitHub Copilot SDK (Node.js) run: cd "${GITHUB_WORKSPACE}" && npm install --ignore-scripts --no-save @github/copilot-sdk@1.0.4 - name: Install TypeScript LSP dependencies - run: npm install -g --ignore-scripts typescript typescript-language-server + run: npm install -g --ignore-scripts typescript@5.8.3 typescript-language-server@4.3.3 env: NPM_CONFIG_MIN_RELEASE_AGE: '3' timeout-minutes: 10 diff --git a/pkg/parser/schemas/main_workflow_schema.json b/pkg/parser/schemas/main_workflow_schema.json index d76df2f3078..d4ff6273c59 100644 --- a/pkg/parser/schemas/main_workflow_schema.json +++ b/pkg/parser/schemas/main_workflow_schema.json @@ -4737,6 +4737,11 @@ "type": "string", "minLength": 1 } + }, + "version": { + "type": "string", + "minLength": 1, + "description": "Package version to install for this language server (e.g. \"5.8.3\"). Overrides the built-in pinned default. Has no effect for custom servers not in the built-in install spec table." } }, "required": ["command", "fileExtensions"], diff --git a/pkg/workflow/lsp_manager.go b/pkg/workflow/lsp_manager.go index 9bba7f88972..944fa08e7b5 100644 --- a/pkg/workflow/lsp_manager.go +++ b/pkg/workflow/lsp_manager.go @@ -16,6 +16,11 @@ type LSPServerConfig struct { Command string `json:"command,omitempty"` Args []string `json:"args,omitempty"` FileExtensions map[string]string `json:"fileExtensions,omitempty"` + // Version pins the package version for the language server. When set, it overrides the + // built-in default version for known LSP servers. Accepts standard semver version strings + // (e.g. "5.3.0") without a leading "v". Has no effect for custom servers not in the + // built-in install spec table. + Version string `json:"version,omitempty"` } // LSPManager handles LSP configuration normalization, validation, and generation. @@ -120,6 +125,12 @@ func (m *LSPManager) GenerateInstallSteps(workflowData *WorkflowData) []GitHubAc continue } + // Resolve effective version: frontmatter overrides the spec default. + // Strip any leading 'v' prefix so both "0.17.0" and "v0.17.0" are handled + // consistently, avoiding malformed version strings like "@vv0.17.0". + config := m.servers[language] + effectiveVersion := strings.TrimPrefix(config.Version, "v") + var step GitHubActionStep if len(spec.NpmPackages) > 0 { // npm-based LSP server: build install command from runtime-manager settings. @@ -127,7 +138,21 @@ func (m *LSPManager) GenerateInstallSteps(workflowData *WorkflowData) []GitHubAc if !runInstallScripts { args = append(args, "--ignore-scripts") } - args = append(args, spec.NpmPackages...) + // Pin each npm package to its version. The primary (last) package is the + // LSP server binary itself; its version can be overridden via the frontmatter + // 'version' field. All other packages use their hardcoded default version. + primaryPkg := spec.NpmPackages[len(spec.NpmPackages)-1] + for _, pkg := range spec.NpmPackages { + ver := spec.NpmPackageVersions[pkg] + if pkg == primaryPkg && effectiveVersion != "" { + ver = effectiveVersion + } + if ver != "" { + args = append(args, pkg+"@"+ver) + } else { + args = append(args, pkg) + } + } installCmd := strings.Join(args, " ") step = GitHubActionStep{ " - name: " + spec.StepName, @@ -141,10 +166,35 @@ func (m *LSPManager) GenerateInstallSteps(workflowData *WorkflowData) []GitHubAc } step = append(step, " timeout-minutes: 10") } else { - // Non-npm LSP server (go install, gem install, rustup): use raw command. + // Non-npm LSP server (go install, gem install, rustup): build versioned command. + var installCmd string + switch language { + case "go": + ver := spec.DefaultVersion + if effectiveVersion != "" { + ver = effectiveVersion + } + if ver != "" { + installCmd = "go install golang.org/x/tools/gopls@v" + ver + } else { + installCmd = "go install golang.org/x/tools/gopls@latest" + } + case "ruby": + ver := spec.DefaultVersion + if effectiveVersion != "" { + ver = effectiveVersion + } + if ver != "" { + installCmd = "gem install solargraph -v " + ver + } else { + installCmd = "gem install solargraph" + } + default: + installCmd = "rustup component add rust-analyzer" + } step = GitHubActionStep{ " - name: " + spec.StepName, - " run: " + spec.Command, + " run: " + installCmd, " timeout-minutes: 10", } } @@ -206,51 +256,59 @@ func (m *LSPManager) RuntimeRequirements() []RuntimeRequirement { } type lspInstallSpec struct { - StepName string - NpmPackages []string // Non-nil: install these packages globally via npm (respects RunInstallScripts + cooldown) - Command string // Non-empty: raw install command for non-npm runtimes (go, gem, rustup) - RuntimeID string // runtime manager ID for the runtime needed to run this LSP server + StepName string + NpmPackages []string // Non-nil: install these packages globally via npm (respects RunInstallScripts + cooldown) + NpmPackageVersions map[string]string // Default pinned version for each npm package; key = package name + DefaultVersion string // Default pinned version for non-npm installs (go, gem) + RuntimeID string // runtime manager ID for the runtime needed to run this LSP server } var lspInstallSpecs = map[string]lspInstallSpec{ "bash": { - StepName: "Install Bash LSP dependencies", - NpmPackages: []string{"bash-language-server"}, - RuntimeID: "node", + StepName: "Install Bash LSP dependencies", + NpmPackages: []string{"bash-language-server"}, + NpmPackageVersions: map[string]string{"bash-language-server": "5.4.0"}, + RuntimeID: "node", }, "go": { - StepName: "Install Go LSP dependencies", - Command: "go install golang.org/x/tools/gopls@latest", - RuntimeID: "go", + StepName: "Install Go LSP dependencies", + DefaultVersion: "0.18.1", + RuntimeID: "go", }, "php": { - StepName: "Install PHP LSP dependencies", - NpmPackages: []string{"intelephense"}, - RuntimeID: "node", + StepName: "Install PHP LSP dependencies", + NpmPackages: []string{"intelephense"}, + NpmPackageVersions: map[string]string{"intelephense": "1.14.1"}, + RuntimeID: "node", }, "python": { - StepName: "Install Python LSP dependencies", - NpmPackages: []string{"pyright"}, - RuntimeID: "node", + StepName: "Install Python LSP dependencies", + NpmPackages: []string{"pyright"}, + NpmPackageVersions: map[string]string{"pyright": "1.1.399"}, + RuntimeID: "node", }, "ruby": { - StepName: "Install Ruby LSP dependencies", - Command: "gem install solargraph", - RuntimeID: "ruby", + StepName: "Install Ruby LSP dependencies", + DefaultVersion: "0.50.0", + RuntimeID: "ruby", }, "rust": { StepName: "Install Rust LSP dependencies", - Command: "rustup component add rust-analyzer", RuntimeID: "", // Rust is not in knownRuntimes; runtime setup is done via rustup }, "typescript": { StepName: "Install TypeScript LSP dependencies", NpmPackages: []string{"typescript", "typescript-language-server"}, - RuntimeID: "node", + NpmPackageVersions: map[string]string{ + "typescript": "5.8.3", + "typescript-language-server": "4.3.3", + }, + RuntimeID: "node", }, "yaml": { - StepName: "Install YAML LSP dependencies", - NpmPackages: []string{"yaml-language-server"}, - RuntimeID: "node", + StepName: "Install YAML LSP dependencies", + NpmPackages: []string{"yaml-language-server"}, + NpmPackageVersions: map[string]string{"yaml-language-server": "1.15.0"}, + RuntimeID: "node", }, } diff --git a/pkg/workflow/lsp_manager_test.go b/pkg/workflow/lsp_manager_test.go index 866f541a5a0..0fc47e8ff45 100644 --- a/pkg/workflow/lsp_manager_test.go +++ b/pkg/workflow/lsp_manager_test.go @@ -70,12 +70,12 @@ func TestLSPManagerGenerateInstallSteps(t *testing.T) { }, }) - // Default: --ignore-scripts + cooldown enabled + // Default: --ignore-scripts + cooldown enabled + pinned versions steps := manager.GenerateInstallSteps(nil) require.Len(t, steps, 1) content := strings.Join(steps[0], "\n") assert.Contains(t, content, "Install TypeScript LSP dependencies") - assert.Contains(t, content, "npm install -g --ignore-scripts typescript typescript-language-server") + assert.Contains(t, content, "npm install -g --ignore-scripts typescript@5.8.3 typescript-language-server@4.3.3") assert.Contains(t, content, "NPM_CONFIG_MIN_RELEASE_AGE") } @@ -95,7 +95,7 @@ func TestLSPManagerGenerateInstallSteps_RunInstallScripts(t *testing.T) { steps := manager.GenerateInstallSteps(workflowData) require.Len(t, steps, 1) content := strings.Join(steps[0], "\n") - assert.Contains(t, content, "npm install -g typescript typescript-language-server") + assert.Contains(t, content, "npm install -g typescript@5.8.3 typescript-language-server@4.3.3") assert.NotContains(t, content, "--ignore-scripts") } @@ -124,6 +124,94 @@ func TestLSPManagerGenerateInstallSteps_CooldownDisabled(t *testing.T) { assert.NotContains(t, content, "NPM_CONFIG_MIN_RELEASE_AGE") } +func TestLSPManagerGenerateInstallSteps_DefaultVersionPinning(t *testing.T) { + // Default pinned versions are injected for known npm-based servers. + manager := NewLSPManager(map[string]LSPServerConfig{ + "yaml": { + Command: "yaml-language-server", + FileExtensions: map[string]string{".yaml": "yaml"}, + }, + }) + steps := manager.GenerateInstallSteps(nil) + require.Len(t, steps, 1) + content := strings.Join(steps[0], "\n") + assert.Contains(t, content, "yaml-language-server@1.15.0") +} + +func TestLSPManagerGenerateInstallSteps_VersionOverride(t *testing.T) { + // A Version field in the frontmatter overrides the pinned default for the primary package. + manager := NewLSPManager(map[string]LSPServerConfig{ + "typescript": { + Command: "typescript-language-server", + Version: "5.0.0", + FileExtensions: map[string]string{".ts": "typescript"}, + }, + }) + steps := manager.GenerateInstallSteps(nil) + require.Len(t, steps, 1) + content := strings.Join(steps[0], "\n") + // typescript-language-server is the primary (last) package; its version is overridden. + assert.Contains(t, content, "typescript-language-server@5.0.0") + // typescript (secondary package) retains its default version. + assert.Contains(t, content, "typescript@5.8.3") +} + +func TestLSPManagerGenerateInstallSteps_GoDefaultVersion(t *testing.T) { + // gopls is installed with its pinned default version. + manager := NewLSPManager(map[string]LSPServerConfig{ + "go": {Command: "gopls", FileExtensions: map[string]string{".go": "go"}}, + }) + steps := manager.GenerateInstallSteps(nil) + require.Len(t, steps, 1) + content := strings.Join(steps[0], "\n") + assert.Contains(t, content, "go install golang.org/x/tools/gopls@v0.18.1") +} + +func TestLSPManagerGenerateInstallSteps_GoVersionOverride(t *testing.T) { + // A Version field overrides gopls install version. + manager := NewLSPManager(map[string]LSPServerConfig{ + "go": {Command: "gopls", Version: "0.17.0", FileExtensions: map[string]string{".go": "go"}}, + }) + steps := manager.GenerateInstallSteps(nil) + require.Len(t, steps, 1) + content := strings.Join(steps[0], "\n") + assert.Contains(t, content, "go install golang.org/x/tools/gopls@v0.17.0") +} + +func TestLSPManagerGenerateInstallSteps_GoVersionOverride_VPrefix(t *testing.T) { + // A Version field with a leading 'v' prefix must not produce '@vv...' in the command. + manager := NewLSPManager(map[string]LSPServerConfig{ + "go": {Command: "gopls", Version: "v0.17.0", FileExtensions: map[string]string{".go": "go"}}, + }) + steps := manager.GenerateInstallSteps(nil) + require.Len(t, steps, 1) + content := strings.Join(steps[0], "\n") + assert.Contains(t, content, "go install golang.org/x/tools/gopls@v0.17.0") + assert.NotContains(t, content, "@vv") +} + +func TestLSPManagerGenerateInstallSteps_RubyDefaultVersion(t *testing.T) { + // solargraph is installed with its pinned default version. + manager := NewLSPManager(map[string]LSPServerConfig{ + "ruby": {Command: "solargraph", FileExtensions: map[string]string{".rb": "ruby"}}, + }) + steps := manager.GenerateInstallSteps(nil) + require.Len(t, steps, 1) + content := strings.Join(steps[0], "\n") + assert.Contains(t, content, "gem install solargraph -v 0.50.0") +} + +func TestLSPManagerGenerateInstallSteps_RubyVersionOverride(t *testing.T) { + // A Version field overrides solargraph gem install version. + manager := NewLSPManager(map[string]LSPServerConfig{ + "ruby": {Command: "solargraph", Version: "0.48.0", FileExtensions: map[string]string{".rb": "ruby"}}, + }) + steps := manager.GenerateInstallSteps(nil) + require.Len(t, steps, 1) + content := strings.Join(steps[0], "\n") + assert.Contains(t, content, "gem install solargraph -v 0.48.0") +} + func TestLSPManagerRuntimeRequirements_NodeBased(t *testing.T) { // Node.js-based LSP servers (typescript, python/pyright, bash, php, yaml) should all // resolve to the "node" runtime — deduplicated to a single requirement. From 6fc5b52f09059c52dbe79b49066d1f64cc2550fc Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 27 Jun 2026 18:49:08 +0000 Subject: [PATCH 13/13] refactor(lsp): move TypeScript LSP test from smoke-codex to smoke-copilot - Revert LSP changes from smoke-codex (engine change from codex to copilot, lsp frontmatter, node runtime v20, test #11 LSP TypeScript) - Add TypeScript LSP server to smoke-copilot with lsp frontmatter and node runtime v20 - Add test #16 LSP TypeScript to smoke-copilot to count functions in actions/setup/js/safe_output_helpers.cjs - Recompile both workflows Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .github/workflows/smoke-codex.lock.yml | 423 ++++++++++++++--------- .github/workflows/smoke-codex.md | 22 +- .github/workflows/smoke-copilot.lock.yml | 14 +- .github/workflows/smoke-copilot.md | 18 +- 4 files changed, 286 insertions(+), 191 deletions(-) diff --git a/.github/workflows/smoke-codex.lock.yml b/.github/workflows/smoke-codex.lock.yml index ebd1e73b4a0..f345d13cd44 100644 --- a/.github/workflows/smoke-codex.lock.yml +++ b/.github/workflows/smoke-codex.lock.yml @@ -1,5 +1,5 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"f66218c1af651228b4ec3d19d0e98c5f28dd35a1030de8e39f23ec226b031511","body_hash":"722513f82c6256103c0cf8abb58e23e79a10dfd972e4fa036a857a315ee30af1","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.65","copilot-sdk":"1.0.4"}} -# gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_GRAFANA_AUTHORIZATION","GH_AW_OTEL_GRAFANA_ENDPOINT","GH_AW_OTEL_SENTRY_AUTHORIZATION","GH_AW_OTEL_SENTRY_ENDPOINT","GITHUB_TOKEN"],"actions":[{"repo":"actions-ecosystem/action-add-labels","sha":"c96b68fec76a0987cd93957189e9abd0b9a72ff1","version":"v1.1.3"},{"repo":"actions/cache/restore","sha":"27d5ce7f107fe9357f9df03efb73ab90386fccae","version":"v5.0.5"},{"repo":"actions/cache/save","sha":"27d5ce7f107fe9357f9df03efb73ab90386fccae","version":"v5.0.5"},{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7.0.0"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-go","sha":"4a3601121dd01d1626a1e23e37211e3254c1c06c","version":"v6.4.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.11","digest":"sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.11@sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.11","digest":"sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.11@sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d"},{"image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.11"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.11","digest":"sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.11@sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.30","digest":"sha256:35625d1a2269b1238606078c879f59a91cffc4ac33eb54bf39c6418822c1a8be","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.30@sha256:35625d1a2269b1238606078c879f59a91cffc4ac33eb54bf39c6418822c1a8be"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.4.0","digest":"sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036","pinned_image":"ghcr.io/github/github-mcp-server:v1.4.0@sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036"},{"image":"ghcr.io/github/serena-mcp-server:latest","digest":"sha256:bf343399e3725c45528f531a230f3a04521d4cdef29f9a5af6282ff0d3c393c5","pinned_image":"ghcr.io/github/serena-mcp-server:latest@sha256:bf343399e3725c45528f531a230f3a04521d4cdef29f9a5af6282ff0d3c393c5"}]} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"42a605ac96d9044f6b6da08a4ebdf7db4c1c2f261b7660d34fabd9d0e9701da0","body_hash":"2148439cf2a717e019b1235ae583d7f6087ecc038accee7a93e3b54b3c24c6b9","strict":true,"agent_id":"codex","engine_versions":{"codex":"0.142.1"}} +# gh-aw-manifest: {"version":1,"secrets":["CODEX_API_KEY","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_GRAFANA_AUTHORIZATION","GH_AW_OTEL_GRAFANA_ENDPOINT","GH_AW_OTEL_SENTRY_AUTHORIZATION","GH_AW_OTEL_SENTRY_ENDPOINT","GITHUB_TOKEN","OPENAI_API_KEY"],"actions":[{"repo":"actions-ecosystem/action-add-labels","sha":"c96b68fec76a0987cd93957189e9abd0b9a72ff1","version":"v1.1.3"},{"repo":"actions/cache/restore","sha":"27d5ce7f107fe9357f9df03efb73ab90386fccae","version":"v5.0.5"},{"repo":"actions/cache/save","sha":"27d5ce7f107fe9357f9df03efb73ab90386fccae","version":"v5.0.5"},{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7.0.0"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-go","sha":"4a3601121dd01d1626a1e23e37211e3254c1c06c","version":"v6.4.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.11","digest":"sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.11@sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.11","digest":"sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.11@sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d"},{"image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.11"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.11","digest":"sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.11@sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.30","digest":"sha256:35625d1a2269b1238606078c879f59a91cffc4ac33eb54bf39c6418822c1a8be","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.30@sha256:35625d1a2269b1238606078c879f59a91cffc4ac33eb54bf39c6418822c1a8be"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.4.0","digest":"sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036","pinned_image":"ghcr.io/github/github-mcp-server:v1.4.0@sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036"},{"image":"ghcr.io/github/serena-mcp-server:latest","digest":"sha256:bf343399e3725c45528f531a230f3a04521d4cdef29f9a5af6282ff0d3c393c5","pinned_image":"ghcr.io/github/serena-mcp-server:latest@sha256:bf343399e3725c45528f531a230f3a04521d4cdef29f9a5af6282ff0d3c393c5"}]} # This file was automatically generated by gh-aw. DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # # ___ _ _ @@ -36,7 +36,7 @@ # - shared/trufflehog.md # # Secrets used: -# - COPILOT_GITHUB_TOKEN +# - CODEX_API_KEY # - GH_AW_GITHUB_MCP_SERVER_TOKEN # - GH_AW_GITHUB_TOKEN # - GH_AW_OTEL_GRAFANA_AUTHORIZATION @@ -44,6 +44,7 @@ # - GH_AW_OTEL_SENTRY_AUTHORIZATION # - GH_AW_OTEL_SENTRY_ENDPOINT # - GITHUB_TOKEN +# - OPENAI_API_KEY # # Custom actions used: # - actions-ecosystem/action-add-labels@c96b68fec76a0987cd93957189e9abd0b9a72ff1 # v1.1.3 @@ -92,7 +93,7 @@ run-name: "Smoke Codex" env: OTEL_EXPORTER_OTLP_ENDPOINT: ${{ secrets.GH_AW_OTEL_SENTRY_ENDPOINT }} OTEL_SERVICE_NAME: gh-aw.smoke-codex - OTEL_RESOURCE_ATTRIBUTES: 'gh-aw.workflow.name=Smoke%20Codex,gh-aw.repository=${{ github.repository }},gh-aw.run.id=${{ github.run_id }},github.run_id=${{ github.run_id }},gh-aw.engine.id=copilot' + OTEL_RESOURCE_ATTRIBUTES: 'gh-aw.workflow.name=Smoke%20Codex,gh-aw.repository=${{ github.repository }},gh-aw.run.id=${{ github.run_id }},github.run_id=${{ github.run_id }},gh-aw.engine.id=codex' OTEL_EXPORTER_OTLP_HEADERS: x-sentry-auth=${{ secrets.GH_AW_OTEL_SENTRY_AUTHORIZATION }} GH_AW_OTLP_ALL_HEADERS: x-sentry-auth=${{ secrets.GH_AW_OTEL_SENTRY_AUTHORIZATION }},Authorization=${{ secrets.GH_AW_OTEL_GRAFANA_AUTHORIZATION }} GH_AW_OTLP_ENDPOINTS: '[{"url":"${{ secrets.GH_AW_OTEL_SENTRY_ENDPOINT }}","headers":"x-sentry-auth=${{ secrets.GH_AW_OTEL_SENTRY_AUTHORIZATION }}"},{"url":"${{ secrets.GH_AW_OTEL_GRAFANA_ENDPOINT }}","headers":"Authorization=${{ secrets.GH_AW_OTEL_GRAFANA_AUTHORIZATION }}"}]' @@ -151,19 +152,19 @@ jobs: env: GH_AW_SETUP_WORKFLOW_NAME: "Smoke Codex" GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/smoke-codex.lock.yml@${{ github.ref }} - GH_AW_INFO_VERSION: "1.0.65" + GH_AW_INFO_VERSION: "0.142.1" GH_AW_INFO_AWF_VERSION: "v0.27.11" - GH_AW_INFO_ENGINE_ID: "copilot" + GH_AW_INFO_ENGINE_ID: "codex" - name: Mask OTLP telemetry headers run: bash "${RUNNER_TEMP}/gh-aw/actions/mask_otlp_headers.sh" - name: Generate agentic run info id: generate_aw_info env: - GH_AW_INFO_ENGINE_ID: "copilot" - GH_AW_INFO_ENGINE_NAME: "GitHub Copilot CLI" - GH_AW_INFO_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || vars.GH_AW_DEFAULT_MODEL_COPILOT || 'claude-sonnet-4.6' }} - GH_AW_INFO_VERSION: "1.0.65" - GH_AW_INFO_AGENT_VERSION: "1.0.65" + GH_AW_INFO_ENGINE_ID: "codex" + GH_AW_INFO_ENGINE_NAME: "Codex" + GH_AW_INFO_MODEL: ${{ vars.GH_AW_MODEL_AGENT_CODEX || vars.GH_AW_DEFAULT_MODEL_CODEX || 'gpt-5.4' }} + GH_AW_INFO_VERSION: "0.142.1" + GH_AW_INFO_AGENT_VERSION: "0.142.1" GH_AW_INFO_WORKFLOW_NAME: "Smoke Codex" GH_AW_INFO_EXPERIMENTAL: "false" GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true" @@ -239,11 +240,12 @@ jobs: setupGlobals(core, github, context, exec, io, getOctokit); const { main } = require('${{ runner.temp }}/gh-aw/actions/add_reaction.cjs'); await main(); - - name: Validate COPILOT_GITHUB_TOKEN secret + - name: Validate CODEX_API_KEY or OPENAI_API_KEY secret id: validate-secret - run: bash "${RUNNER_TEMP}/gh-aw/actions/validate_multi_secret.sh" COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default + run: bash "${RUNNER_TEMP}/gh-aw/actions/validate_multi_secret.sh" CODEX_API_KEY OPENAI_API_KEY Codex https://github.github.com/gh-aw/reference/engines/#openai-codex env: - COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} + CODEX_API_KEY: ${{ secrets.CODEX_API_KEY }} + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} - name: Checkout .github and .agents folders uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: @@ -283,7 +285,7 @@ jobs: id: sanitized uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: - GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,*.grafana.net,*.sentry.io,127.0.0.1,::1,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,app.renovatebot.com,appveyor.com,archive.ubuntu.com,azure.archive.ubuntu.com,badgen.net,bun.sh,cdn.jsdelivr.net,cdn.playwright.dev,circleci.com,codacy.com,codeclimate.com,codecov.io,codeload.github.com,coveralls.io,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,deb.nodesource.com,deepsource.io,deno.land,docs.github.com,drone.io,esm.sh,get.pnpm.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,go.dev,golang.org,googleapis.deno.dev,googlechromelabs.github.io,goproxy.io,host.docker.internal,img.shields.io,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,lfs.github.com,localhost,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,patch-diff.githubusercontent.com,pkg.go.dev,playwright.download.prss.microsoft.com,ppa.launchpad.net,proxy.golang.org,raw.githubusercontent.com,readthedocs.io,readthedocs.org,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,renovatebot.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,semaphoreci.com,shields.io,skimdb.npmjs.com,snyk.io,sonarcloud.io,sonarqube.com,storage.googleapis.com,sum.golang.org,telemetry.enterprise.githubcopilot.com,telemetry.vercel.com,travis-ci.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com,www.npmjs.com,www.npmjs.org,yarnpkg.com" + GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,*.grafana.net,*.sentry.io,127.0.0.1,172.30.0.1,::1,api.github.com,api.openai.com,api.snapcraft.io,app.renovatebot.com,appveyor.com,archive.ubuntu.com,azure.archive.ubuntu.com,badgen.net,cdn.playwright.dev,chatgpt.com,circleci.com,codacy.com,codeclimate.com,codecov.io,codeload.github.com,coveralls.io,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,deepsource.io,docs.github.com,drone.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,go.dev,golang.org,goproxy.io,host.docker.internal,img.shields.io,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,localhost,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,openai.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,patch-diff.githubusercontent.com,pkg.go.dev,playwright.download.prss.microsoft.com,ppa.launchpad.net,proxy.golang.org,raw.githubusercontent.com,readthedocs.io,readthedocs.org,renovatebot.com,s.symcb.com,s.symcd.com,security.ubuntu.com,semaphoreci.com,shields.io,snyk.io,sonarcloud.io,sonarqube.com,storage.googleapis.com,sum.golang.org,travis-ci.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com" with: script: | const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); @@ -409,7 +411,7 @@ jobs: uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - GH_AW_ENGINE_ID: "copilot" + GH_AW_ENGINE_ID: "codex" GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} @@ -496,8 +498,8 @@ jobs: /tmp/gh-aw/aw-prompts/prompt-import-tree.json /tmp/gh-aw/github_rate_limits.jsonl /tmp/gh-aw/base - /tmp/gh-aw/.github/agents - /tmp/gh-aw/.github/skills + /tmp/gh-aw/.codex/agents + /tmp/gh-aw/.codex/skills if-no-files-found: ignore retention-days: 1 @@ -557,9 +559,9 @@ jobs: env: GH_AW_SETUP_WORKFLOW_NAME: "Smoke Codex" GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/smoke-codex.lock.yml@${{ github.ref }} - GH_AW_INFO_VERSION: "1.0.65" + GH_AW_INFO_VERSION: "0.142.1" GH_AW_INFO_AWF_VERSION: "v0.27.11" - GH_AW_INFO_ENGINE_ID: "copilot" + GH_AW_INFO_ENGINE_ID: "codex" - name: Set runtime paths id: set-runtime-paths run: | @@ -582,11 +584,6 @@ jobs: cache: false - name: Capture GOROOT for AWF chroot mode run: echo "GOROOT=$(go env GOROOT)" >> "$GITHUB_ENV" - - name: Setup Node.js - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 - with: - node-version: '20' - package-manager-cache: false - name: Create gh-aw temp directory run: bash "${RUNNER_TEMP}/gh-aw/actions/create_gh_aw_tmp_dir.sh" - name: Configure gh CLI for GitHub Enterprise @@ -629,19 +626,15 @@ jobs: setupGlobals(core, github, context, exec, io, getOctokit); const { main } = require('${{ runner.temp }}/gh-aw/actions/checkout_pr_branch.cjs'); await main(); - - name: Install GitHub Copilot CLI - run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" 1.0.65 - env: - GH_HOST: github.com + - name: Setup Node.js + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + with: + node-version: '24' + package-manager-cache: false + - name: Install Codex CLI + run: npm install --ignore-scripts -g @openai/codex@0.142.1 - name: Install AWF binary run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.27.11 - - name: Install GitHub Copilot SDK (Node.js) - run: cd "${GITHUB_WORKSPACE}" && npm install --ignore-scripts --no-save @github/copilot-sdk@1.0.4 - - name: Install TypeScript LSP dependencies - run: npm install -g --ignore-scripts typescript@5.8.3 typescript-language-server@4.3.3 - env: - NPM_CONFIG_MIN_RELEASE_AGE: '3' - timeout-minutes: 10 - name: Install Playwright CLI run: npm install -g @playwright/cli@0.1.14 env: @@ -684,12 +677,12 @@ jobs: run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_base_github_folders.sh" - name: Restore inline sub-agents from activation artifact env: - GH_AW_SUB_AGENT_DIR: ".github/agents" - GH_AW_SUB_AGENT_EXT: ".agent.md" + GH_AW_SUB_AGENT_DIR: ".codex/agents" + GH_AW_SUB_AGENT_EXT: ".md" run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_inline_sub_agents.sh" - name: Restore inline skills from activation artifact env: - GH_AW_SKILL_DIR: ".github/skills" + GH_AW_SKILL_DIR: ".codex/skills" run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_inline_skills.sh" - name: Download container images run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.27.11@sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7 ghcr.io/github/gh-aw-firewall/api-proxy:0.27.11@sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.11 ghcr.io/github/gh-aw-firewall/squid:0.27.11@sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d ghcr.io/github/gh-aw-mcpg:v0.3.30@sha256:35625d1a2269b1238606078c879f59a91cffc4ac33eb54bf39c6418822c1a8be ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b ghcr.io/github/github-mcp-server:v1.4.0@sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036 ghcr.io/github/serena-mcp-server:latest@sha256:bf343399e3725c45528f531a230f3a04521d4cdef29f9a5af6282ff0d3c393c5 @@ -1032,6 +1025,7 @@ jobs: - name: Start MCP Gateway id: start-mcp-gateway env: + CODEX_HOME: /tmp/gh-aw/mcp-config GH_AW_POLICY_ALLOW_CREATE_PULL_REQUEST: ${{ vars.GH_AW_POLICY_ALLOW_CREATE_PULL_REQUEST || 'true' }} GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS_CONFIG_PATH }} @@ -1053,7 +1047,7 @@ jobs: export MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD="524288" export DEBUG="*" - export GH_AW_ENGINE="copilot" + export GH_AW_ENGINE="codex" export GH_AW_MCP_CLI_SERVERS='["safeoutputs","serena"]' MCP_GATEWAY_UID=$(id -u 2>/dev/null || echo '0') MCP_GATEWAY_GID=$(id -g 2>/dev/null || echo '0') @@ -1063,15 +1057,51 @@ jobs: * ) DOCKER_SOCK_PATH=/var/run/docker.sock ;; esac DOCKER_SOCK_GID=$(stat -c '%g' "$DOCKER_SOCK_PATH" 2>/dev/null || echo '0') - export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host --name awmg-mcpg --add-host host.docker.internal:127.0.0.1 --user '"${MCP_GATEWAY_UID}"':'"${MCP_GATEWAY_GID}"' --group-add '"${DOCKER_SOCK_GID}"' -v '"${DOCKER_SOCK_PATH}"':/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DOCKER_HOST=unix:///var/run/docker.sock -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_POLICY_ALLOW_CREATE_PULL_REQUEST -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e RUNNER_TEMP -e GITHUB_AW_OTEL_TRACE_ID -e GITHUB_AW_OTEL_PARENT_SPAN_ID -e OTEL_EXPORTER_OTLP_HEADERS -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw -v '"${RUNNER_TEMP}"'/gh-aw/safeoutputs:'"${RUNNER_TEMP}"'/gh-aw/safeoutputs:rw ghcr.io/github/gh-aw-mcpg:v0.3.30' + export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host --name awmg-mcpg --add-host host.docker.internal:127.0.0.1 --user '"${MCP_GATEWAY_UID}"':'"${MCP_GATEWAY_GID}"' --group-add '"${DOCKER_SOCK_GID}"' -v '"${DOCKER_SOCK_PATH}"':/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DOCKER_HOST=unix:///var/run/docker.sock -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_POLICY_ALLOW_CREATE_PULL_REQUEST -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e RUNNER_TEMP -e GITHUB_AW_OTEL_TRACE_ID -e GITHUB_AW_OTEL_PARENT_SPAN_ID -e OTEL_EXPORTER_OTLP_HEADERS -e CODEX_HOME -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw -v '"${RUNNER_TEMP}"'/gh-aw/safeoutputs:'"${RUNNER_TEMP}"'/gh-aw/safeoutputs:rw ghcr.io/github/gh-aw-mcpg:v0.3.30' + + cat > "${RUNNER_TEMP}/gh-aw/mcp-config/config.toml" << GH_AW_MCP_CONFIG_88a23f25b9e52e24_EOF + [history] + persistence = "none" + + [shell_environment_policy] + inherit = "core" + include_only = ["^CODEX_API_KEY$", "^GH_AW_ASSETS_ALLOWED_EXTS$", "^GH_AW_ASSETS_BRANCH$", "^GH_AW_ASSETS_MAX_SIZE_KB$", "^GH_AW_SAFE_OUTPUTS$", "^GITHUB_REPOSITORY$", "^GITHUB_SERVER_URL$", "^HOME$", "^OPENAI_API_KEY$", "^PATH$"] + + [mcp_servers.safeoutputs] + container = "ghcr.io/github/gh-aw-node" + mounts = ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw", "${RUNNER_TEMP}/gh-aw/safeoutputs:${RUNNER_TEMP}/gh-aw/safeoutputs:rw", "/tmp/gh-aw:/tmp/gh-aw:rw"] + args = ["-w", "$GITHUB_WORKSPACE"] + entrypoint = "sh" + entrypointArgs = ["-c", "sh ${RUNNER_TEMP}/gh-aw/safeoutputs/start_safe_outputs_mcp.sh"] + env_vars = ["DEBUG", "DEFAULT_BRANCH", "GH_AW_ASSETS_ALLOWED_EXTS", "GH_AW_ASSETS_BRANCH", "GH_AW_ASSETS_MAX_SIZE_KB", "GH_AW_MCP_LOG_DIR", "GH_AW_SAFE_OUTPUTS", "GH_AW_SAFE_OUTPUTS_CONFIG_PATH", "GH_AW_SAFE_OUTPUTS_TOOLS_PATH", "GH_AW_POLICY_ALLOW_CREATE_PULL_REQUEST", "GITHUB_REPOSITORY", "GITHUB_TOKEN", "GITHUB_WORKSPACE", "RUNNER_TEMP"] + + [mcp_servers.safeoutputs."guard-policies"] + + [mcp_servers.safeoutputs."guard-policies".write-sink] + accept = ["*"] + + [mcp_servers.serena] + container = "ghcr.io/github/serena-mcp-server:latest" + entrypoint = "serena" + entrypointArgs = ["start-mcp-server", "--context", "codex", "--project", "\${GITHUB_WORKSPACE}"] + mounts = ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw"] + args = [ + "--network", + "host", + ] + + [mcp_servers.serena."guard-policies"] + + [mcp_servers.serena."guard-policies".write-sink] + accept = ["*"] + GH_AW_MCP_CONFIG_88a23f25b9e52e24_EOF - mkdir -p "$HOME/.copilot" + # Generate JSON config for MCP gateway GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node) - cat << GH_AW_MCP_CONFIG_40aaf988fe3a4dfe_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" + cat << GH_AW_MCP_CONFIG_8cb9074ab13d6252_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" { "mcpServers": { "safeoutputs": { - "type": "stdio", "container": "ghcr.io/github/gh-aw-node", "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw", "${RUNNER_TEMP}/gh-aw/safeoutputs:${RUNNER_TEMP}/gh-aw/safeoutputs:rw", "/tmp/gh-aw:/tmp/gh-aw:rw"], "args": ["-w", "\${GITHUB_WORKSPACE}"], @@ -1102,34 +1132,31 @@ jobs: } }, "serena": { - "type": "stdio", - "container": "ghcr.io/github/serena-mcp-server:latest", - "entrypoint": "serena", - "entrypointArgs": [ - "start-mcp-server", - "--context", - "codex", - "--project", - "\${GITHUB_WORKSPACE}" - ], - "mounts": [ - "\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw" - ], - "args": [ - "--network", - "host" - ], - "tools": [ - "*" - ], - "guard-policies": { - "write-sink": { - "accept": [ - "*" - ] - } + "type": "stdio", + "container": "ghcr.io/github/serena-mcp-server:latest", + "entrypoint": "serena", + "entrypointArgs": [ + "start-mcp-server", + "--context", + "codex", + "--project", + "\${GITHUB_WORKSPACE}" + ], + "mounts": [ + "\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw" + ], + "args": [ + "--network", + "host" + ], + "guard-policies": { + "write-sink": { + "accept": [ + "*" + ] } } + } }, "gateway": { "port": $MCP_GATEWAY_PORT, @@ -1143,7 +1170,34 @@ jobs: } } } - GH_AW_MCP_CONFIG_40aaf988fe3a4dfe_EOF + GH_AW_MCP_CONFIG_8cb9074ab13d6252_EOF + + # Sync converter output to writable CODEX_HOME for Codex + mkdir -p /tmp/gh-aw/mcp-config + cat > "/tmp/gh-aw/mcp-config/config.toml" << GH_AW_CODEX_SHELL_POLICY_d8326fb8068eb0d4_EOF + + model_provider = "openai-proxy" + + [model_providers.openai-proxy] + name = "OpenAI AWF proxy" + base_url = "http://172.30.0.30:10000" + env_key = "OPENAI_API_KEY" + supports_websockets = false + [shell_environment_policy] + inherit = "core" + include_only = ["^CODEX_API_KEY$", "^GH_AW_ASSETS_ALLOWED_EXTS$", "^GH_AW_ASSETS_BRANCH$", "^GH_AW_ASSETS_MAX_SIZE_KB$", "^GH_AW_SAFE_OUTPUTS$", "^GITHUB_REPOSITORY$", "^GITHUB_SERVER_URL$", "^HOME$", "^OPENAI_API_KEY$", "^PATH$"] + GH_AW_CODEX_SHELL_POLICY_d8326fb8068eb0d4_EOF + awk ' + BEGIN { skip_openai_proxy = 0 } + /^[[:space:]]*model_provider[[:space:]]*=/ { next } + /^\[model_providers\.openai-proxy\][[:space:]]*$/ { skip_openai_proxy = 1; next } + /^\[/ { skip_openai_proxy = 0 } + !skip_openai_proxy { print } + ' "${RUNNER_TEMP}/gh-aw/mcp-config/config.toml" >> "/tmp/gh-aw/mcp-config/config.toml" + chmod 600 "/tmp/gh-aw/mcp-config/config.toml" + mkdir -p "${CODEX_HOME}" + if [ "/tmp/gh-aw/mcp-config/config.toml" != "${CODEX_HOME}/config.toml" ]; then cp "/tmp/gh-aw/mcp-config/config.toml" "${CODEX_HOME}/config.toml"; fi + chmod 600 "${CODEX_HOME}/config.toml" - name: Mount MCP servers as CLIs id: mount-mcp-clis continue-on-error: true @@ -1173,25 +1227,15 @@ jobs: CLI_PROXY_IMAGE: 'ghcr.io/github/gh-aw-mcpg:v0.3.30' run: | bash "${RUNNER_TEMP}/gh-aw/actions/start_cli_proxy.sh" - - name: Execute GitHub Copilot CLI + - name: Execute Codex CLI id: agentic_execution - # Copilot CLI tool arguments (sorted): - timeout-minutes: 15 run: | set -o pipefail printf '%s' "$(date +%s%3N)" > /tmp/gh-aw/agent_cli_start_ms.txt - trap 'rm -f "$HOME/.copilot/settings.json"' EXIT - mkdir -p "$HOME/.copilot" - printf '%s' '{"builtInAgents":{"rubberDuck":false},"lspServers":{"typescript":{"command":"typescript-language-server","args":["--stdio"],"fileExtensions":{".cjs":"javascript",".js":"javascript",".mjs":"javascript",".ts":"typescript",".tsx":"typescriptreact"}}}}' > "$HOME/.copilot/settings.json" - export XDG_CONFIG_HOME="$HOME" - export GH_AW_MCP_CONFIG="$HOME/.copilot/mcp-config.json" - touch /tmp/gh-aw/agent-step-summary.md - GH_AW_NODE_BIN=$(command -v node 2>/dev/null || true) - export GH_AW_NODE_BIN - export COPILOT_API_KEY="$COPILOT_DUMMY_BYOK" + mkdir -p "$CODEX_HOME/logs" && touch /tmp/gh-aw/agent-step-summary.md (umask 177 && touch /tmp/gh-aw/agent-stdio.log) - GH_AW_MAX_AI_CREDITS="${GH_AW_MAX_AI_CREDITS:-1000}" - printf '%s\n' "{\"\$schema\":\"https://github.com/github/gh-aw-firewall/releases/download/v0.27.11/awf-config.schema.json\",\"network\":{\"allowDomains\":[\"*.githubusercontent.com\",\"*.grafana.net\",\"*.sentry.io\",\"api.business.githubcopilot.com\",\"api.enterprise.githubcopilot.com\",\"api.github.com\",\"api.githubcopilot.com\",\"api.individual.githubcopilot.com\",\"api.npms.io\",\"api.snapcraft.io\",\"archive.ubuntu.com\",\"azure.archive.ubuntu.com\",\"bun.sh\",\"cdn.jsdelivr.net\",\"cdn.playwright.dev\",\"codeload.github.com\",\"crl.geotrust.com\",\"crl.globalsign.com\",\"crl.identrust.com\",\"crl.sectigo.com\",\"crl.thawte.com\",\"crl.usertrust.com\",\"crl.verisign.com\",\"crl3.digicert.com\",\"crl4.digicert.com\",\"crls.ssl.com\",\"deb.nodesource.com\",\"deno.land\",\"docs.github.com\",\"esm.sh\",\"get.pnpm.io\",\"github-cloud.githubusercontent.com\",\"github-cloud.s3.amazonaws.com\",\"github.blog\",\"github.com\",\"github.githubassets.com\",\"go.dev\",\"golang.org\",\"googleapis.deno.dev\",\"googlechromelabs.github.io\",\"goproxy.io\",\"host.docker.internal\",\"json-schema.org\",\"json.schemastore.org\",\"jsr.io\",\"keyserver.ubuntu.com\",\"lfs.github.com\",\"nodejs.org\",\"npm.pkg.github.com\",\"npmjs.com\",\"npmjs.org\",\"objects.githubusercontent.com\",\"ocsp.digicert.com\",\"ocsp.geotrust.com\",\"ocsp.globalsign.com\",\"ocsp.identrust.com\",\"ocsp.sectigo.com\",\"ocsp.ssl.com\",\"ocsp.thawte.com\",\"ocsp.usertrust.com\",\"ocsp.verisign.com\",\"packagecloud.io\",\"packages.cloud.google.com\",\"packages.microsoft.com\",\"patch-diff.githubusercontent.com\",\"pkg.go.dev\",\"playwright.download.prss.microsoft.com\",\"ppa.launchpad.net\",\"proxy.golang.org\",\"raw.githubusercontent.com\",\"registry.bower.io\",\"registry.npmjs.com\",\"registry.npmjs.org\",\"registry.yarnpkg.com\",\"repo.yarnpkg.com\",\"s.symcb.com\",\"s.symcd.com\",\"security.ubuntu.com\",\"skimdb.npmjs.com\",\"storage.googleapis.com\",\"sum.golang.org\",\"telemetry.enterprise.githubcopilot.com\",\"telemetry.vercel.com\",\"ts-crl.ws.symantec.com\",\"ts-ocsp.ws.symantec.com\",\"www.googleapis.com\",\"www.npmjs.com\",\"www.npmjs.org\",\"yarnpkg.com\"]},\"apiProxy\":{\"enabled\":true,\"enableTokenSteering\":true,\"maxRuns\":500,\"maxAiCredits\":${GH_AW_MAX_AI_CREDITS},\"maxCacheMisses\":5,\"models\":{\"agent\":[\"sonnet-6x\",\"gpt-5.5\",\"gpt-5.4\",\"gpt-5.3\",\"gemini-pro\",\"any\"],\"antigravity\":[\"copilot/antigravity*\",\"google/antigravity*\",\"gemini/antigravity*\"],\"any\":[\"copilot/*\",\"anthropic/*\",\"openai/*\",\"google/*\",\"gemini/*\"],\"claude\":[\"agent\"],\"codex\":[\"agent\"],\"coding\":[\"copilot/gpt-5*codex*\",\"openai/gpt-5*codex*\",\"gpt-5-codex\"],\"computer-use\":[\"copilot/*computer-use*\",\"google/*computer-use*\",\"gemini/*computer-use*\",\"openai/*computer-use*\"],\"copilot\":[\"agent\"],\"deep-research\":[\"copilot/deep-research*\",\"copilot/o3-deep-research*\",\"copilot/o4-mini-deep-research*\",\"google/deep-research*\",\"gemini/deep-research*\",\"openai/o3-deep-research*\",\"openai/o4-mini-deep-research*\"],\"gemini\":[\"agent\"],\"gemini-3-flash\":[\"copilot/gemini-3*flash*\",\"google/gemini-3*flash*\",\"gemini/gemini-3*flash*\"],\"gemini-3-pro\":[\"copilot/gemini-3*pro*\",\"google/gemini-3*pro*\",\"google/nano-banana*\",\"gemini/gemini-3*pro*\"],\"gemini-3.1-flash\":[\"copilot/gemini-3.1*flash*\",\"google/gemini-3.1*flash*\",\"gemini/gemini-3.1*flash*\"],\"gemini-3.1-pro\":[\"copilot/gemini-3.1*pro*\",\"google/gemini-3.1*pro*\",\"gemini/gemini-3.1*pro*\"],\"gemini-3.5-flash\":[\"copilot/gemini-3.5*flash*\",\"google/gemini-3.5*flash*\",\"gemini/gemini-3.5*flash*\"],\"gemini-flash\":[\"copilot/gemini-*flash*\",\"google/gemini-*flash*\",\"gemini/gemini-*flash*\"],\"gemini-flash-lite\":[\"copilot/gemini-*flash*lite*\",\"google/gemini-*flash*lite*\",\"gemini/gemini-*flash*lite*\"],\"gemini-pro\":[\"copilot/gemini-*pro*\",\"google/gemini-*pro*\",\"gemini/gemini-*pro*\"],\"gemma\":[\"copilot/gemma*\",\"google/gemma*\",\"gemini/gemma*\"],\"gpt-5\":[\"copilot/gpt-5*\",\"openai/gpt-5*\"],\"gpt-5-codex\":[\"copilot/gpt-5*codex*\",\"openai/gpt-5*codex*\"],\"gpt-5-mini\":[\"copilot/gpt-5*mini*\",\"openai/gpt-5*mini*\"],\"gpt-5-nano\":[\"copilot/gpt-5*nano*\",\"openai/gpt-5*nano*\"],\"gpt-5-pro\":[\"copilot/gpt-5*pro*\",\"openai/gpt-5*pro*\"],\"gpt-5.1\":[\"copilot/gpt-5.1*\",\"openai/gpt-5.1*\"],\"gpt-5.2\":[\"copilot/gpt-5.2*\",\"openai/gpt-5.2*\"],\"gpt-5.3\":[\"copilot/gpt-5.3*\",\"openai/gpt-5.3*\"],\"gpt-5.4\":[\"copilot/gpt-5.4*\",\"openai/gpt-5.4*\"],\"gpt-5.5\":[\"copilot/gpt-5.5*\",\"openai/gpt-5.5*\"],\"haiku\":[\"copilot/*haiku*\",\"anthropic/*haiku*\"],\"image-generation\":[\"copilot/gpt-image*\",\"openai/gpt-image*\",\"openai/chatgpt-image*\",\"copilot/gemini-*image*\",\"google/gemini-*image*\",\"gemini/gemini-*image*\",\"google/imagen*\"],\"large\":[\"sonnet\",\"gpt-5-pro\",\"gpt-5\",\"gemini-pro\"],\"mai-code\":[\"copilot/MAI-Code*\",\"copilot/mai-code*\",\"openai/MAI-Code*\"],\"mini\":[\"haiku\",\"gpt-5-mini\",\"gpt-5-nano\",\"gemini-flash-lite\"],\"nano-banana\":[\"copilot/nano-banana*\",\"google/nano-banana*\",\"gemini/nano-banana*\"],\"opus\":[\"copilot/*opus*\",\"anthropic/*opus*\"],\"opusplan\":[\"opus?effort=high\"],\"reasoning\":[\"copilot/o1*\",\"copilot/o3*\",\"copilot/o4*\",\"openai/o1*\",\"openai/o3*\",\"openai/o4*\"],\"robotics\":[\"copilot/*robotics*\",\"google/*robotics*\",\"gemini/*robotics*\"],\"small\":[\"mini\"],\"small-agent\":[\"haiku\",\"gpt-5-mini\",\"gemini-flash\"],\"sonnet\":[\"copilot/*sonnet*\",\"anthropic/*sonnet*\"],\"sonnet-6x\":[\"copilot/*sonnet-4.5*\",\"copilot/*sonnet-4.6*\",\"copilot/*sonnet-4-5-*\",\"anthropic/*sonnet-4-5-*\",\"copilot/*sonnet-4-6*\",\"anthropic/*sonnet-4-6*\"],\"summarization\":[\"haiku\",\"gpt-5-mini\",\"gemini-flash-lite\",\"mini\"],\"vision\":[\"copilot/gemini-*image*\",\"google/gemini-*image*\",\"gemini/gemini-*image*\",\"copilot/gemini-*flash*\",\"google/gemini-*flash*\",\"gemini/gemini-*flash*\"]}},\"container\":{\"imageTag\":\"0.27.11,squid=sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d,agent=sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7,api-proxy=sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d\"}}" > "${RUNNER_TEMP}/gh-aw/awf-config.json" + GH_AW_MAX_AI_CREDITS="${{ vars.GH_AW_DEFAULT_MAX_AI_CREDITS || '1000' }}" + printf '%s\n' "{\"\$schema\":\"https://github.com/github/gh-aw-firewall/releases/download/v0.27.11/awf-config.schema.json\",\"network\":{\"allowDomains\":[\"*.githubusercontent.com\",\"*.grafana.net\",\"*.sentry.io\",\"172.30.0.1\",\"api.github.com\",\"api.openai.com\",\"api.snapcraft.io\",\"archive.ubuntu.com\",\"azure.archive.ubuntu.com\",\"cdn.playwright.dev\",\"chatgpt.com\",\"codeload.github.com\",\"crl.geotrust.com\",\"crl.globalsign.com\",\"crl.identrust.com\",\"crl.sectigo.com\",\"crl.thawte.com\",\"crl.usertrust.com\",\"crl.verisign.com\",\"crl3.digicert.com\",\"crl4.digicert.com\",\"crls.ssl.com\",\"docs.github.com\",\"github-cloud.githubusercontent.com\",\"github-cloud.s3.amazonaws.com\",\"github.blog\",\"github.com\",\"github.githubassets.com\",\"go.dev\",\"golang.org\",\"goproxy.io\",\"host.docker.internal\",\"json-schema.org\",\"json.schemastore.org\",\"keyserver.ubuntu.com\",\"lfs.github.com\",\"objects.githubusercontent.com\",\"ocsp.digicert.com\",\"ocsp.geotrust.com\",\"ocsp.globalsign.com\",\"ocsp.identrust.com\",\"ocsp.sectigo.com\",\"ocsp.ssl.com\",\"ocsp.thawte.com\",\"ocsp.usertrust.com\",\"ocsp.verisign.com\",\"openai.com\",\"packagecloud.io\",\"packages.cloud.google.com\",\"packages.microsoft.com\",\"patch-diff.githubusercontent.com\",\"pkg.go.dev\",\"playwright.download.prss.microsoft.com\",\"ppa.launchpad.net\",\"proxy.golang.org\",\"raw.githubusercontent.com\",\"s.symcb.com\",\"s.symcd.com\",\"security.ubuntu.com\",\"storage.googleapis.com\",\"sum.golang.org\",\"ts-crl.ws.symantec.com\",\"ts-ocsp.ws.symantec.com\",\"www.googleapis.com\"]},\"apiProxy\":{\"enabled\":true,\"enableTokenSteering\":true,\"maxRuns\":500,\"maxAiCredits\":${GH_AW_MAX_AI_CREDITS},\"maxCacheMisses\":5,\"models\":{\"agent\":[\"sonnet-6x\",\"gpt-5.5\",\"gpt-5.4\",\"gpt-5.3\",\"gemini-pro\",\"any\"],\"antigravity\":[\"copilot/antigravity*\",\"google/antigravity*\",\"gemini/antigravity*\"],\"any\":[\"copilot/*\",\"anthropic/*\",\"openai/*\",\"google/*\",\"gemini/*\"],\"claude\":[\"agent\"],\"codex\":[\"agent\"],\"coding\":[\"copilot/gpt-5*codex*\",\"openai/gpt-5*codex*\",\"gpt-5-codex\"],\"computer-use\":[\"copilot/*computer-use*\",\"google/*computer-use*\",\"gemini/*computer-use*\",\"openai/*computer-use*\"],\"copilot\":[\"agent\"],\"deep-research\":[\"copilot/deep-research*\",\"copilot/o3-deep-research*\",\"copilot/o4-mini-deep-research*\",\"google/deep-research*\",\"gemini/deep-research*\",\"openai/o3-deep-research*\",\"openai/o4-mini-deep-research*\"],\"gemini\":[\"agent\"],\"gemini-3-flash\":[\"copilot/gemini-3*flash*\",\"google/gemini-3*flash*\",\"gemini/gemini-3*flash*\"],\"gemini-3-pro\":[\"copilot/gemini-3*pro*\",\"google/gemini-3*pro*\",\"google/nano-banana*\",\"gemini/gemini-3*pro*\"],\"gemini-3.1-flash\":[\"copilot/gemini-3.1*flash*\",\"google/gemini-3.1*flash*\",\"gemini/gemini-3.1*flash*\"],\"gemini-3.1-pro\":[\"copilot/gemini-3.1*pro*\",\"google/gemini-3.1*pro*\",\"gemini/gemini-3.1*pro*\"],\"gemini-3.5-flash\":[\"copilot/gemini-3.5*flash*\",\"google/gemini-3.5*flash*\",\"gemini/gemini-3.5*flash*\"],\"gemini-flash\":[\"copilot/gemini-*flash*\",\"google/gemini-*flash*\",\"gemini/gemini-*flash*\"],\"gemini-flash-lite\":[\"copilot/gemini-*flash*lite*\",\"google/gemini-*flash*lite*\",\"gemini/gemini-*flash*lite*\"],\"gemini-pro\":[\"copilot/gemini-*pro*\",\"google/gemini-*pro*\",\"gemini/gemini-*pro*\"],\"gemma\":[\"copilot/gemma*\",\"google/gemma*\",\"gemini/gemma*\"],\"gpt-5\":[\"copilot/gpt-5*\",\"openai/gpt-5*\"],\"gpt-5-codex\":[\"copilot/gpt-5*codex*\",\"openai/gpt-5*codex*\"],\"gpt-5-mini\":[\"copilot/gpt-5*mini*\",\"openai/gpt-5*mini*\"],\"gpt-5-nano\":[\"copilot/gpt-5*nano*\",\"openai/gpt-5*nano*\"],\"gpt-5-pro\":[\"copilot/gpt-5*pro*\",\"openai/gpt-5*pro*\"],\"gpt-5.1\":[\"copilot/gpt-5.1*\",\"openai/gpt-5.1*\"],\"gpt-5.2\":[\"copilot/gpt-5.2*\",\"openai/gpt-5.2*\"],\"gpt-5.3\":[\"copilot/gpt-5.3*\",\"openai/gpt-5.3*\"],\"gpt-5.4\":[\"copilot/gpt-5.4*\",\"openai/gpt-5.4*\"],\"gpt-5.5\":[\"copilot/gpt-5.5*\",\"openai/gpt-5.5*\"],\"haiku\":[\"copilot/*haiku*\",\"anthropic/*haiku*\"],\"image-generation\":[\"copilot/gpt-image*\",\"openai/gpt-image*\",\"openai/chatgpt-image*\",\"copilot/gemini-*image*\",\"google/gemini-*image*\",\"gemini/gemini-*image*\",\"google/imagen*\"],\"large\":[\"sonnet\",\"gpt-5-pro\",\"gpt-5\",\"gemini-pro\"],\"mai-code\":[\"copilot/MAI-Code*\",\"copilot/mai-code*\",\"openai/MAI-Code*\"],\"mini\":[\"haiku\",\"gpt-5-mini\",\"gpt-5-nano\",\"gemini-flash-lite\"],\"nano-banana\":[\"copilot/nano-banana*\",\"google/nano-banana*\",\"gemini/nano-banana*\"],\"opus\":[\"copilot/*opus*\",\"anthropic/*opus*\"],\"opusplan\":[\"opus?effort=high\"],\"reasoning\":[\"copilot/o1*\",\"copilot/o3*\",\"copilot/o4*\",\"openai/o1*\",\"openai/o3*\",\"openai/o4*\"],\"robotics\":[\"copilot/*robotics*\",\"google/*robotics*\",\"gemini/*robotics*\"],\"small\":[\"mini\"],\"small-agent\":[\"haiku\",\"gpt-5-mini\",\"gemini-flash\"],\"sonnet\":[\"copilot/*sonnet*\",\"anthropic/*sonnet*\"],\"sonnet-6x\":[\"copilot/*sonnet-4.5*\",\"copilot/*sonnet-4.6*\",\"copilot/*sonnet-4-5-*\",\"anthropic/*sonnet-4-5-*\",\"copilot/*sonnet-4-6*\",\"anthropic/*sonnet-4-6*\"],\"summarization\":[\"haiku\",\"gpt-5-mini\",\"gemini-flash-lite\",\"mini\"],\"vision\":[\"copilot/gemini-*image*\",\"google/gemini-*image*\",\"gemini/gemini-*image*\",\"copilot/gemini-*flash*\",\"google/gemini-*flash*\",\"gemini/gemini-*flash*\"]}},\"container\":{\"imageTag\":\"0.27.11,squid=sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d,agent=sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7,api-proxy=sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d\"}}" > "${RUNNER_TEMP}/gh-aw/awf-config.json" cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json export GH_AW_MODELS_JSON_PATH="/tmp/gh-aw/models.json" GH_AW_DOCKER_HOST="" @@ -1211,41 +1255,28 @@ jobs: fi fi # shellcheck disable=SC1003,SC2086 - sudo -E awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" ${GH_AW_TOOL_CACHE_MOUNT:+--mount "$GH_AW_TOOL_CACHE_MOUNT"} ${GH_AW_DOCKER_HOST:+--docker-host "$GH_AW_DOCKER_HOST"} ${GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS} --env-all --exclude-env COPILOT_GITHUB_TOKEN --exclude-env GH_TOKEN --exclude-env GITHUB_MCP_SERVER_TOKEN --exclude-env MCP_GATEWAY_API_KEY --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --allow-host-ports 80,443,8080 --skip-pull --difc-proxy-host host.docker.internal:18443 --difc-proxy-ca-cert /tmp/gh-aw/difc-proxy-tls/ca.crt \ - -- /bin/bash -c 'set +o histexpand; export PATH="${RUNNER_TEMP}/gh-aw/mcp-cli/bin:$PATH" && : "${RUNNER_TOOL_CACHE:?RUNNER_TOOL_CACHE must be set}"; GH_AW_TOOL_CACHE="$RUNNER_TOOL_CACHE"; export PATH="$(find "$GH_AW_TOOL_CACHE" -maxdepth 5 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || true)"; fi; if [ -z "$GH_AW_NODE_EXEC" ]; then echo "node runtime missing on this runner — check runtimes.node in workflow YAML" >&2; exit 127; fi; GH_AW_WORKSPACE_NODE_MODULES="${GITHUB_WORKSPACE:-$PWD}/node_modules"; if [ -d "$GH_AW_WORKSPACE_NODE_MODULES" ]; then export NODE_PATH="${GH_AW_WORKSPACE_NODE_MODULES}${NODE_PATH:+:${NODE_PATH}}"; fi; GH_AW_NPM_GLOBAL_ROOT="$(npm root -g 2>/dev/null || true)"; if [ -n "$GH_AW_NPM_GLOBAL_ROOT" ]; then export NODE_PATH="${GH_AW_NPM_GLOBAL_ROOT}${NODE_PATH:+:${NODE_PATH}}"; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/copilot_harness.cjs "$GH_AW_NODE_EXEC" "${RUNNER_TEMP}/gh-aw/actions/copilot_sdk_driver.cjs" /usr/local/bin/copilot' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log - env: - AWF_REFLECT_ENABLED: 1 - COPILOT_AGENT_RUNNER_TYPE: STANDALONE - COPILOT_DUMMY_BYOK: dummy-byok-key-for-offline-mode - COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} - COPILOT_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || vars.GH_AW_DEFAULT_MODEL_COPILOT || 'claude-sonnet-4.6' }} - COPILOT_SDK_URI: http://127.0.0.1:3002 - GH_AW_COPILOT_SDK_DRIVER: 1 - GH_AW_COPILOT_SDK_SERVER_ARGS: '["--headless","--no-auto-update","--port","3002","--add-dir","/tmp/gh-aw/","--log-level","all","--log-dir","/tmp/gh-aw/sandbox/agent/logs/","--disable-builtin-mcps","--no-ask-user","--allow-all-tools","--add-dir","/tmp/gh-aw/cache-memory/","--allow-all-paths"]' - GH_AW_LLM_PROVIDER: github - GH_AW_MAX_AI_CREDITS: ${{ vars.GH_AW_DEFAULT_MAX_AI_CREDITS || '1000' }} - GH_AW_MAX_TOOL_DENIALS: 5 + sudo -E awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" ${GH_AW_TOOL_CACHE_MOUNT:+--mount "$GH_AW_TOOL_CACHE_MOUNT"} ${GH_AW_DOCKER_HOST:+--docker-host "$GH_AW_DOCKER_HOST"} ${GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS} --env-all --exclude-env CODEX_API_KEY --exclude-env GH_TOKEN --exclude-env GITHUB_MCP_SERVER_TOKEN --exclude-env MCP_GATEWAY_API_KEY --exclude-env OPENAI_API_KEY --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --allow-host-ports 80,443,8080 --skip-pull --difc-proxy-host host.docker.internal:18443 --difc-proxy-ca-cert /tmp/gh-aw/difc-proxy-tls/ca.crt \ + -- /bin/bash -c 'set +o histexpand; export PATH="${RUNNER_TEMP}/gh-aw/mcp-cli/bin:$PATH" && : "${RUNNER_TOOL_CACHE:?RUNNER_TOOL_CACHE must be set}"; GH_AW_TOOL_CACHE="$RUNNER_TOOL_CACHE"; export PATH="$(find "$GH_AW_TOOL_CACHE" -maxdepth 5 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || true)"; fi; if [ -z "$GH_AW_NODE_EXEC" ]; then echo "node runtime missing on this runner — check runtimes.node in workflow YAML" >&2; exit 127; fi; GH_AW_NPM_GLOBAL_ROOT="$(npm root -g 2>/dev/null || true)"; if [ -n "$GH_AW_NPM_GLOBAL_ROOT" ]; then export NODE_PATH="${GH_AW_NPM_GLOBAL_ROOT}${NODE_PATH:+:${NODE_PATH}}"; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/codex_harness.cjs codex exec${GH_AW_MODEL_AGENT_CODEX:+ --model "$GH_AW_MODEL_AGENT_CODEX"} -c web_search="disabled" --dangerously-bypass-approvals-and-sandbox --skip-git-repo-check --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log + env: + CODEX_API_KEY: ${{ secrets.CODEX_API_KEY || secrets.OPENAI_API_KEY }} + CODEX_HOME: /tmp/gh-aw/mcp-config GH_AW_MAX_TURNS: ${{ vars.GH_AW_DEFAULT_MAX_TURNS || '' }} + GH_AW_MCP_CONFIG: ${{ runner.temp }}/gh-aw/mcp-config/config.toml + GH_AW_MODEL_AGENT_CODEX: ${{ vars.GH_AW_MODEL_AGENT_CODEX || vars.GH_AW_DEFAULT_MODEL_CODEX || 'gpt-5.4' }} GH_AW_PHASE: agent GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} - GH_AW_TIMEOUT_MINUTES: 15 GH_AW_VERSION: dev GH_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || github.token }} - GITHUB_API_URL: ${{ github.api_url }} GITHUB_AW: true - GITHUB_COPILOT_INTEGRATION_ID: agentic-workflows - GITHUB_HEAD_REF: ${{ github.head_ref }} - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - GITHUB_REF_NAME: ${{ github.ref_name }} - GITHUB_SERVER_URL: ${{ github.server_url }} GITHUB_STEP_SUMMARY: /tmp/gh-aw/agent-step-summary.md - GITHUB_WORKSPACE: ${{ github.workspace }} GIT_AUTHOR_EMAIL: github-actions[bot]@users.noreply.github.com GIT_AUTHOR_NAME: github-actions[bot] GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com GIT_COMMITTER_NAME: github-actions[bot] + OPENAI_API_KEY: ${{ secrets.CODEX_API_KEY || secrets.OPENAI_API_KEY }} RUNNER_TEMP: ${{ runner.temp }} + RUST_LOG: ${{ runner.debug == 1 && 'trace,hyper_util=info,mio=info,reqwest=info,os_info=info,codex_otel=warn,codex_core=debug,ocodex_exec=debug' || 'warn' }} TRACEPARENT: ${{ env.GITHUB_AW_OTEL_TRACE_ID != '' && env.GITHUB_AW_OTEL_PARENT_SPAN_ID != '' && format('00-{0}-{1}-01', env.GITHUB_AW_OTEL_TRACE_ID, env.GITHUB_AW_OTEL_PARENT_SPAN_ID) || '' }} - name: Stop CLI Proxy if: always() @@ -1262,10 +1293,6 @@ jobs: GITHUB_SERVER_URL: ${{ github.server_url }} GITHUB_TOKEN: ${{ github.token }} run: bash "${RUNNER_TEMP}/gh-aw/actions/configure_git_credentials.sh" - - name: Copy Copilot session state files to logs - if: always() - continue-on-error: true - run: bash "${RUNNER_TEMP}/gh-aw/actions/copy_copilot_session_state.sh" - name: Stop MCP Gateway if: always() continue-on-error: true @@ -1285,11 +1312,12 @@ jobs: const { main } = require('${{ runner.temp }}/gh-aw/actions/redact_secrets.cjs'); await main(); env: - GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN' - SECRET_COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} + GH_AW_SECRET_NAMES: 'CODEX_API_KEY,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN,OPENAI_API_KEY' + SECRET_CODEX_API_KEY: ${{ secrets.CODEX_API_KEY }} SECRET_GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }} SECRET_GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }} SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SECRET_OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} - name: Append agent step summary if: always() run: bash "${RUNNER_TEMP}/gh-aw/actions/append_agent_step_summary.sh" @@ -1306,7 +1334,7 @@ jobs: uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} - GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,*.grafana.net,*.sentry.io,127.0.0.1,::1,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,app.renovatebot.com,appveyor.com,archive.ubuntu.com,azure.archive.ubuntu.com,badgen.net,bun.sh,cdn.jsdelivr.net,cdn.playwright.dev,circleci.com,codacy.com,codeclimate.com,codecov.io,codeload.github.com,coveralls.io,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,deb.nodesource.com,deepsource.io,deno.land,docs.github.com,drone.io,esm.sh,get.pnpm.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,go.dev,golang.org,googleapis.deno.dev,googlechromelabs.github.io,goproxy.io,host.docker.internal,img.shields.io,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,lfs.github.com,localhost,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,patch-diff.githubusercontent.com,pkg.go.dev,playwright.download.prss.microsoft.com,ppa.launchpad.net,proxy.golang.org,raw.githubusercontent.com,readthedocs.io,readthedocs.org,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,renovatebot.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,semaphoreci.com,shields.io,skimdb.npmjs.com,snyk.io,sonarcloud.io,sonarqube.com,storage.googleapis.com,sum.golang.org,telemetry.enterprise.githubcopilot.com,telemetry.vercel.com,travis-ci.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com,www.npmjs.com,www.npmjs.org,yarnpkg.com" + GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,*.grafana.net,*.sentry.io,127.0.0.1,172.30.0.1,::1,api.github.com,api.openai.com,api.snapcraft.io,app.renovatebot.com,appveyor.com,archive.ubuntu.com,azure.archive.ubuntu.com,badgen.net,cdn.playwright.dev,chatgpt.com,circleci.com,codacy.com,codeclimate.com,codecov.io,codeload.github.com,coveralls.io,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,deepsource.io,docs.github.com,drone.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,go.dev,golang.org,goproxy.io,host.docker.internal,img.shields.io,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,localhost,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,openai.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,patch-diff.githubusercontent.com,pkg.go.dev,playwright.download.prss.microsoft.com,ppa.launchpad.net,proxy.golang.org,raw.githubusercontent.com,readthedocs.io,readthedocs.org,renovatebot.com,s.symcb.com,s.symcd.com,security.ubuntu.com,semaphoreci.com,shields.io,snyk.io,sonarcloud.io,sonarqube.com,storage.googleapis.com,sum.golang.org,travis-ci.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com" GITHUB_SERVER_URL: ${{ github.server_url }} GITHUB_API_URL: ${{ github.api_url }} GH_AW_COMMANDS: "[\"smoke-codex\"]" @@ -1320,12 +1348,12 @@ jobs: if: always() uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: - GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/ + GH_AW_AGENT_OUTPUT: /tmp/gh-aw/agent-stdio.log with: script: | const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); setupGlobals(core, github, context, exec, io, getOctokit); - const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_copilot_log.cjs'); + const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_codex_log.cjs'); await main(); - name: Parse MCP Gateway logs for step summary if: always() @@ -1413,7 +1441,7 @@ jobs: name: agent path: | /tmp/gh-aw/aw-prompts/prompt.txt - /tmp/gh-aw/sandbox/agent/logs/ + /tmp/gh-aw/mcp-config/logs/ /tmp/gh-aw/redacted-urls.log /tmp/gh-aw/mcp-logs/ /tmp/gh-aw/agent_usage.json @@ -1481,9 +1509,9 @@ jobs: env: GH_AW_SETUP_WORKFLOW_NAME: "Smoke Codex" GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/smoke-codex.lock.yml@${{ github.ref }} - GH_AW_INFO_VERSION: "1.0.65" + GH_AW_INFO_VERSION: "0.142.1" GH_AW_INFO_AWF_VERSION: "v0.27.11" - GH_AW_INFO_ENGINE_ID: "copilot" + GH_AW_INFO_ENGINE_ID: "codex" - name: Report TruffleHog secret scan failure if: always() && needs.trufflehog_scan.result == 'failure' && needs.trufflehog_scan.outputs.secrets_found == 'true' uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 @@ -1691,7 +1719,7 @@ jobs: GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }} GH_AW_WORKFLOW_ID: "smoke-codex" GH_AW_ACTION_FAILURE_ISSUE_EXPIRES_HOURS: "12" - GH_AW_ENGINE_ID: "copilot" + GH_AW_ENGINE_ID: "codex" GH_AW_SECRET_VERIFICATION_RESULT: ${{ needs.activation.outputs.secret_verification_result }} GH_AW_CHECKOUT_PR_SUCCESS: ${{ needs.agent.outputs.checkout_pr_success }} GH_AW_EFFECTIVE_TOKENS: ${{ needs.agent.outputs.effective_tokens || '' }} @@ -1704,7 +1732,7 @@ jobs: GH_AW_MCP_POLICY_ERROR: ${{ needs.agent.outputs.mcp_policy_error }} GH_AW_AGENTIC_ENGINE_TIMEOUT: ${{ needs.agent.outputs.agentic_engine_timeout }} GH_AW_MODEL_NOT_SUPPORTED_ERROR: ${{ needs.agent.outputs.model_not_supported_error }} - GH_AW_ENGINE_API_HOSTS: "api.enterprise.githubcopilot.com,api.githubcopilot.com,api.business.githubcopilot.com,api.individual.githubcopilot.com" + GH_AW_ENGINE_API_HOSTS: "api.openai.com" GH_AW_LOCKDOWN_CHECK_FAILED: ${{ needs.activation.outputs.lockdown_check_failed }} GH_AW_STALE_LOCK_FILE_FAILED: ${{ needs.activation.outputs.stale_lock_file_failed }} GH_AW_DAILY_AI_CREDITS_EXCEEDED: ${{ needs.activation.outputs.daily_ai_credits_exceeded }} @@ -1782,9 +1810,9 @@ jobs: env: GH_AW_SETUP_WORKFLOW_NAME: "Smoke Codex" GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/smoke-codex.lock.yml@${{ github.ref }} - GH_AW_INFO_VERSION: "1.0.65" + GH_AW_INFO_VERSION: "0.142.1" GH_AW_INFO_AWF_VERSION: "v0.27.11" - GH_AW_INFO_ENGINE_ID: "copilot" + GH_AW_INFO_ENGINE_ID: "codex" - name: Download agent output artifact id: download-agent-output continue-on-error: true @@ -1809,8 +1837,6 @@ jobs: run: | rm -rf /tmp/gh-aw/sandbox/firewall/logs rm -rf /tmp/gh-aw/sandbox/firewall/audit - - name: Download container images - run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.27.11@sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7 ghcr.io/github/gh-aw-firewall/api-proxy:0.27.11@sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d ghcr.io/github/gh-aw-firewall/squid:0.27.11@sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d - name: Check if detection needed id: detection_guard if: always() @@ -1872,32 +1898,102 @@ jobs: with: node-version: '24' package-manager-cache: false - - name: Install GitHub Copilot CLI - run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" 1.0.65 - env: - GH_HOST: github.com + - name: Install Codex CLI + run: npm install --ignore-scripts -g @openai/codex@0.142.1 - name: Install AWF binary run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.27.11 - - name: Execute GitHub Copilot CLI + - name: Download container images + run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.27.11@sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7 ghcr.io/github/gh-aw-firewall/api-proxy:0.27.11@sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d ghcr.io/github/gh-aw-firewall/squid:0.27.11@sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d ghcr.io/github/gh-aw-mcpg:v0.3.30@sha256:35625d1a2269b1238606078c879f59a91cffc4ac33eb54bf39c6418822c1a8be + - name: Start MCP Gateway + id: start-mcp-gateway + env: + CODEX_HOME: /tmp/gh-aw/mcp-config + run: | + set -eo pipefail + mkdir -p "${RUNNER_TEMP}/gh-aw/mcp-config" + + # Export gateway environment variables for MCP config and gateway script + export MCP_GATEWAY_PORT="8080" + export MCP_GATEWAY_DOMAIN="host.docker.internal" + export MCP_GATEWAY_HOST_DOMAIN="localhost" + MCP_GATEWAY_API_KEY=$(openssl rand -base64 45 | tr -d '/+=') + echo "::add-mask::${MCP_GATEWAY_API_KEY}" + export MCP_GATEWAY_API_KEY + export MCP_GATEWAY_PAYLOAD_DIR="/tmp/gh-aw/mcp-payloads" + mkdir -p "${MCP_GATEWAY_PAYLOAD_DIR}" + export MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD="524288" + export DEBUG="*" + + export GH_AW_ENGINE="codex" + MCP_GATEWAY_UID=$(id -u 2>/dev/null || echo '0') + MCP_GATEWAY_GID=$(id -g 2>/dev/null || echo '0') + case "${DOCKER_HOST:-}" in + unix://* ) DOCKER_SOCK_PATH="${DOCKER_HOST#unix://}" ;; + /* ) DOCKER_SOCK_PATH="$DOCKER_HOST" ;; + * ) DOCKER_SOCK_PATH=/var/run/docker.sock ;; + esac + DOCKER_SOCK_GID=$(stat -c '%g' "$DOCKER_SOCK_PATH" 2>/dev/null || echo '0') + export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host --name awmg-mcpg --add-host host.docker.internal:127.0.0.1 --user '"${MCP_GATEWAY_UID}"':'"${MCP_GATEWAY_GID}"' --group-add '"${DOCKER_SOCK_GID}"' -v '"${DOCKER_SOCK_PATH}"':/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DOCKER_HOST=unix:///var/run/docker.sock -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_POLICY_ALLOW_CREATE_PULL_REQUEST -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e RUNNER_TEMP -e CODEX_HOME -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw -v '"${RUNNER_TEMP}"'/gh-aw/safeoutputs:'"${RUNNER_TEMP}"'/gh-aw/safeoutputs:rw ghcr.io/github/gh-aw-mcpg:v0.3.30' + + cat > "${RUNNER_TEMP}/gh-aw/mcp-config/config.toml" << GH_AW_MCP_CONFIG_2f5d9885311152cf_EOF + [history] + persistence = "none" + + [shell_environment_policy] + inherit = "core" + include_only = ["^CODEX_API_KEY$", "^HOME$", "^OPENAI_API_KEY$", "^PATH$"] + GH_AW_MCP_CONFIG_2f5d9885311152cf_EOF + + # Generate JSON config for MCP gateway + GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node) + cat << GH_AW_MCP_CONFIG_b9b3d113c1a9458e_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" + { + "mcpServers": { + }, + "gateway": { + "port": $MCP_GATEWAY_PORT, + "domain": "${MCP_GATEWAY_DOMAIN}", + "apiKey": "${MCP_GATEWAY_API_KEY}", + "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}" + } + } + GH_AW_MCP_CONFIG_b9b3d113c1a9458e_EOF + + # Sync converter output to writable CODEX_HOME for Codex + mkdir -p /tmp/gh-aw/mcp-config + cat > "/tmp/gh-aw/mcp-config/config.toml" << GH_AW_CODEX_SHELL_POLICY_f48e0018706875a8_EOF + model_provider = "openai-proxy" + [model_providers.openai-proxy] + name = "OpenAI AWF proxy" + base_url = "http://172.30.0.30:10000" + env_key = "OPENAI_API_KEY" + supports_websockets = false + [shell_environment_policy] + inherit = "core" + include_only = ["^CODEX_API_KEY$", "^HOME$", "^OPENAI_API_KEY$", "^PATH$"] + GH_AW_CODEX_SHELL_POLICY_f48e0018706875a8_EOF + awk ' + BEGIN { skip_openai_proxy = 0 } + /^[[:space:]]*model_provider[[:space:]]*=/ { next } + /^\[model_providers\.openai-proxy\][[:space:]]*$/ { skip_openai_proxy = 1; next } + /^\[/ { skip_openai_proxy = 0 } + !skip_openai_proxy { print } + ' "${RUNNER_TEMP}/gh-aw/mcp-config/config.toml" >> "/tmp/gh-aw/mcp-config/config.toml" + chmod 600 "/tmp/gh-aw/mcp-config/config.toml" + mkdir -p "${CODEX_HOME}" + if [ "/tmp/gh-aw/mcp-config/config.toml" != "${CODEX_HOME}/config.toml" ]; then cp "/tmp/gh-aw/mcp-config/config.toml" "${CODEX_HOME}/config.toml"; fi + chmod 600 "${CODEX_HOME}/config.toml" + - name: Execute Codex CLI if: always() && steps.detection_guard.outputs.run_detection == 'true' continue-on-error: true id: detection_agentic_execution - # Copilot CLI tool arguments (sorted): - timeout-minutes: 20 run: | set -o pipefail printf '%s' "$(date +%s%3N)" > /tmp/gh-aw/agent_cli_start_ms.txt - trap 'rm -f "$HOME/.copilot/settings.json"' EXIT - mkdir -p "$HOME/.copilot" - printf '%s' '{"builtInAgents":{"rubberDuck":false}}' > "$HOME/.copilot/settings.json" - export XDG_CONFIG_HOME="$HOME" - touch /tmp/gh-aw/agent-step-summary.md - GH_AW_NODE_BIN=$(command -v node 2>/dev/null || true) - export GH_AW_NODE_BIN - export COPILOT_API_KEY="$COPILOT_DUMMY_BYOK" + mkdir -p "$CODEX_HOME/logs" && touch /tmp/gh-aw/agent-step-summary.md && mkdir -p /tmp/gh-aw/threat-detection && printf '%s' '{"type":"object","properties":{"prompt_injection":{"type":"boolean"},"secret_leak":{"type":"boolean"},"malicious_patch":{"type":"boolean"},"reasons":{"type":"array","items":{"type":"string"}}},"required":["prompt_injection","secret_leak","malicious_patch","reasons"],"additionalProperties":false}' > /tmp/gh-aw/threat-detection/detection_schema.json (umask 177 && touch /tmp/gh-aw/threat-detection/detection.log) - GH_AW_MAX_AI_CREDITS="${GH_AW_MAX_AI_CREDITS:-400}" - printf '%s\n' "{\"\$schema\":\"https://github.com/github/gh-aw-firewall/releases/download/v0.27.11/awf-config.schema.json\",\"network\":{\"allowDomains\":[\"api.business.githubcopilot.com\",\"api.enterprise.githubcopilot.com\",\"api.github.com\",\"api.githubcopilot.com\",\"api.individual.githubcopilot.com\",\"github.com\",\"host.docker.internal\",\"registry.npmjs.org\",\"telemetry.enterprise.githubcopilot.com\"]},\"apiProxy\":{\"enabled\":true,\"enableTokenSteering\":true,\"maxRuns\":500,\"maxAiCredits\":${GH_AW_MAX_AI_CREDITS},\"maxCacheMisses\":5},\"container\":{\"imageTag\":\"0.27.11,squid=sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d,agent=sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7,api-proxy=sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d\"}}" > "${RUNNER_TEMP}/gh-aw/awf-config.json" + GH_AW_MAX_AI_CREDITS="${{ vars.GH_AW_DEFAULT_DETECTION_MAX_AI_CREDITS || '400' }}" + printf '%s\n' "{\"\$schema\":\"https://github.com/github/gh-aw-firewall/releases/download/v0.27.11/awf-config.schema.json\",\"network\":{\"allowDomains\":[\"172.30.0.1\",\"api.github.com\",\"api.openai.com\",\"chatgpt.com\",\"github.com\",\"host.docker.internal\",\"openai.com\"]},\"apiProxy\":{\"enabled\":true,\"enableTokenSteering\":true,\"maxRuns\":500,\"maxAiCredits\":${GH_AW_MAX_AI_CREDITS},\"maxCacheMisses\":5},\"container\":{\"imageTag\":\"0.27.11,squid=sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d,agent=sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7,api-proxy=sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d\"}}" > "${RUNNER_TEMP}/gh-aw/awf-config.json" cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json export GH_AW_MODELS_JSON_PATH="/tmp/gh-aw/models.json" GH_AW_DOCKER_HOST="" @@ -1919,34 +2015,26 @@ jobs: fi fi # shellcheck disable=SC1003,SC2086 - sudo -E awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" ${GH_AW_TOOL_CACHE_MOUNT:+--mount "$GH_AW_TOOL_CACHE_MOUNT"} ${GH_AW_DOCKER_HOST:+--docker-host "$GH_AW_DOCKER_HOST"} ${GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS} --env-all --exclude-env COPILOT_GITHUB_TOKEN --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --allow-host-ports 80,443,8080 --skip-pull \ - -- /bin/bash -c 'set +o histexpand; : "${RUNNER_TOOL_CACHE:?RUNNER_TOOL_CACHE must be set}"; GH_AW_TOOL_CACHE="$RUNNER_TOOL_CACHE"; export PATH="$(find "$GH_AW_TOOL_CACHE" -maxdepth 5 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || true)"; fi; if [ -z "$GH_AW_NODE_EXEC" ]; then echo "node runtime missing on this runner — check runtimes.node in workflow YAML" >&2; exit 127; fi; GH_AW_NPM_GLOBAL_ROOT="$(npm root -g 2>/dev/null || true)"; if [ -n "$GH_AW_NPM_GLOBAL_ROOT" ]; then export NODE_PATH="${GH_AW_NPM_GLOBAL_ROOT}${NODE_PATH:+:${NODE_PATH}}"; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/copilot_harness.cjs /usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --no-ask-user --allow-all-tools --add-dir "${GITHUB_WORKSPACE}" --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log - env: - AWF_REFLECT_ENABLED: 1 - COPILOT_AGENT_RUNNER_TYPE: STANDALONE - COPILOT_DUMMY_BYOK: dummy-byok-key-for-offline-mode - COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} - COPILOT_MODEL: ${{ vars.GH_AW_MODEL_DETECTION_COPILOT || vars.GH_AW_DEFAULT_MODEL_COPILOT || 'claude-sonnet-4.6' }} - GH_AW_LLM_PROVIDER: github - GH_AW_MAX_AI_CREDITS: ${{ vars.GH_AW_DEFAULT_DETECTION_MAX_AI_CREDITS || '400' }} + sudo -E awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" ${GH_AW_TOOL_CACHE_MOUNT:+--mount "$GH_AW_TOOL_CACHE_MOUNT"} ${GH_AW_DOCKER_HOST:+--docker-host "$GH_AW_DOCKER_HOST"} ${GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS} --env-all --exclude-env CODEX_API_KEY --exclude-env OPENAI_API_KEY --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --allow-host-ports 80,443,8080 --skip-pull \ + -- /bin/bash -c 'set +o histexpand; : "${RUNNER_TOOL_CACHE:?RUNNER_TOOL_CACHE must be set}"; GH_AW_TOOL_CACHE="$RUNNER_TOOL_CACHE"; export PATH="$(find "$GH_AW_TOOL_CACHE" -maxdepth 5 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || true)"; fi; if [ -z "$GH_AW_NODE_EXEC" ]; then echo "node runtime missing on this runner — check runtimes.node in workflow YAML" >&2; exit 127; fi; GH_AW_NPM_GLOBAL_ROOT="$(npm root -g 2>/dev/null || true)"; if [ -n "$GH_AW_NPM_GLOBAL_ROOT" ]; then export NODE_PATH="${GH_AW_NPM_GLOBAL_ROOT}${NODE_PATH:+:${NODE_PATH}}"; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/codex_harness.cjs codex exec${GH_AW_MODEL_DETECTION_CODEX:+ --model "$GH_AW_MODEL_DETECTION_CODEX"} -c web_search="disabled" -c fetch="disabled" --dangerously-bypass-approvals-and-sandbox --skip-git-repo-check --output-schema /tmp/gh-aw/threat-detection/detection_schema.json -o /tmp/gh-aw/threat-detection/detection_result.json --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log + env: + CODEX_API_KEY: ${{ secrets.CODEX_API_KEY || secrets.OPENAI_API_KEY }} + CODEX_HOME: /tmp/gh-aw/mcp-config GH_AW_MAX_TURNS: ${{ vars.GH_AW_DEFAULT_MAX_TURNS || '' }} + GH_AW_MCP_CONFIG: ${{ runner.temp }}/gh-aw/mcp-config/config.toml + GH_AW_MODEL_DETECTION_CODEX: ${{ vars.GH_AW_MODEL_DETECTION_CODEX || vars.GH_AW_DEFAULT_MODEL_CODEX || 'gpt-5.4' }} GH_AW_PHASE: detection GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - GH_AW_TIMEOUT_MINUTES: 20 GH_AW_VERSION: dev - GITHUB_API_URL: ${{ github.api_url }} GITHUB_AW: true - GITHUB_COPILOT_INTEGRATION_ID: agentic-workflows - GITHUB_HEAD_REF: ${{ github.head_ref }} - GITHUB_REF_NAME: ${{ github.ref_name }} - GITHUB_SERVER_URL: ${{ github.server_url }} GITHUB_STEP_SUMMARY: /tmp/gh-aw/agent-step-summary.md - GITHUB_WORKSPACE: ${{ github.workspace }} GIT_AUTHOR_EMAIL: github-actions[bot]@users.noreply.github.com GIT_AUTHOR_NAME: github-actions[bot] GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com GIT_COMMITTER_NAME: github-actions[bot] + OPENAI_API_KEY: ${{ secrets.CODEX_API_KEY || secrets.OPENAI_API_KEY }} RUNNER_TEMP: ${{ runner.temp }} + RUST_LOG: ${{ runner.debug == 1 && 'trace,hyper_util=info,mio=info,reqwest=info,os_info=info,codex_otel=warn,codex_core=debug,ocodex_exec=debug' || 'warn' }} TRACEPARENT: ${{ env.GITHUB_AW_OTEL_TRACE_ID != '' && env.GITHUB_AW_OTEL_PARENT_SPAN_ID != '' && format('00-{0}-{1}-01', env.GITHUB_AW_OTEL_TRACE_ID, env.GITHUB_AW_OTEL_PARENT_SPAN_ID) || '' }} - name: Parse threat detection token usage for step summary id: parse_detection_token_usage @@ -2033,9 +2121,9 @@ jobs: env: GH_AW_SETUP_WORKFLOW_NAME: "Smoke Codex" GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/smoke-codex.lock.yml@${{ github.ref }} - GH_AW_INFO_VERSION: "1.0.65" + GH_AW_INFO_VERSION: "0.142.1" GH_AW_INFO_AWF_VERSION: "v0.27.11" - GH_AW_INFO_ENGINE_ID: "copilot" + GH_AW_INFO_ENGINE_ID: "codex" - name: Check team membership for command workflow id: check_membership uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 @@ -2082,9 +2170,8 @@ jobs: GH_AW_DETECTION_CONCLUSION: ${{ needs.detection.outputs.detection_conclusion }} GH_AW_DETECTION_REASON: ${{ needs.detection.outputs.detection_reason }} GH_AW_EFFECTIVE_TOKENS: ${{ needs.agent.outputs.effective_tokens }} - GH_AW_ENGINE_ID: "copilot" + GH_AW_ENGINE_ID: "codex" GH_AW_ENGINE_MODEL: ${{ needs.agent.outputs.model }} - GH_AW_ENGINE_VERSION: "1.0.65" GH_AW_PROJECT_UTC: "-08:00" GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }} GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e 🔮 *The oracle has spoken through [{workflow_name}]({run_url})*{ai_credits_suffix}{history_link}\",\"runStarted\":\"🔮 The ancient spirits stir... [{workflow_name}]({run_url}) awakens to divine this {event_type}...\",\"runSuccess\":\"✨ The prophecy is fulfilled... [{workflow_name}]({run_url}) has completed its mystical journey. The stars align. 🌟\",\"runFailure\":\"🌑 The shadows whisper... [{workflow_name}]({run_url}) {status}. The oracle requires further meditation...\"}" @@ -2123,9 +2210,9 @@ jobs: env: GH_AW_SETUP_WORKFLOW_NAME: "Smoke Codex" GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/smoke-codex.lock.yml@${{ github.ref }} - GH_AW_INFO_VERSION: "1.0.65" + GH_AW_INFO_VERSION: "0.142.1" GH_AW_INFO_AWF_VERSION: "v0.27.11" - GH_AW_INFO_ENGINE_ID: "copilot" + GH_AW_INFO_ENGINE_ID: "codex" - name: Mask OTLP telemetry headers run: bash "${RUNNER_TEMP}/gh-aw/actions/mask_otlp_headers.sh" - name: Download agent output artifact @@ -2157,7 +2244,7 @@ jobs: env: GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} GH_AW_COMMENT_ID: ${{ needs.activation.outputs.comment_id }} - GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,*.grafana.net,*.sentry.io,127.0.0.1,::1,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,app.renovatebot.com,appveyor.com,archive.ubuntu.com,azure.archive.ubuntu.com,badgen.net,bun.sh,cdn.jsdelivr.net,cdn.playwright.dev,circleci.com,codacy.com,codeclimate.com,codecov.io,codeload.github.com,coveralls.io,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,deb.nodesource.com,deepsource.io,deno.land,docs.github.com,drone.io,esm.sh,get.pnpm.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,go.dev,golang.org,googleapis.deno.dev,googlechromelabs.github.io,goproxy.io,host.docker.internal,img.shields.io,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,lfs.github.com,localhost,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,patch-diff.githubusercontent.com,pkg.go.dev,playwright.download.prss.microsoft.com,ppa.launchpad.net,proxy.golang.org,raw.githubusercontent.com,readthedocs.io,readthedocs.org,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,renovatebot.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,semaphoreci.com,shields.io,skimdb.npmjs.com,snyk.io,sonarcloud.io,sonarqube.com,storage.googleapis.com,sum.golang.org,telemetry.enterprise.githubcopilot.com,telemetry.vercel.com,travis-ci.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com,www.npmjs.com,www.npmjs.org,yarnpkg.com" + GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,*.grafana.net,*.sentry.io,127.0.0.1,172.30.0.1,::1,api.github.com,api.openai.com,api.snapcraft.io,app.renovatebot.com,appveyor.com,archive.ubuntu.com,azure.archive.ubuntu.com,badgen.net,cdn.playwright.dev,chatgpt.com,circleci.com,codacy.com,codeclimate.com,codecov.io,codeload.github.com,coveralls.io,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,deepsource.io,docs.github.com,drone.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,go.dev,golang.org,goproxy.io,host.docker.internal,img.shields.io,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,localhost,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,openai.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,patch-diff.githubusercontent.com,pkg.go.dev,playwright.download.prss.microsoft.com,ppa.launchpad.net,proxy.golang.org,raw.githubusercontent.com,readthedocs.io,readthedocs.org,renovatebot.com,s.symcb.com,s.symcd.com,security.ubuntu.com,semaphoreci.com,shields.io,snyk.io,sonarcloud.io,sonarqube.com,storage.googleapis.com,sum.golang.org,travis-ci.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com" GITHUB_SERVER_URL: ${{ github.server_url }} GITHUB_API_URL: ${{ github.api_url }} GH_AW_SAFE_OUTPUT_ACTIONS: "{\"add_smoked_label\":\"add_smoked_label\"}" @@ -2367,9 +2454,9 @@ jobs: env: GH_AW_SETUP_WORKFLOW_NAME: "Smoke Codex" GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/smoke-codex.lock.yml@${{ github.ref }} - GH_AW_INFO_VERSION: "1.0.65" + GH_AW_INFO_VERSION: "0.142.1" GH_AW_INFO_AWF_VERSION: "v0.27.11" - GH_AW_INFO_ENGINE_ID: "copilot" + GH_AW_INFO_ENGINE_ID: "codex" - name: Download cache-memory artifact (default) id: download_cache_default uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 diff --git a/.github/workflows/smoke-codex.md b/.github/workflows/smoke-codex.md index ea2ff750266..159e8ebb169 100644 --- a/.github/workflows/smoke-codex.md +++ b/.github/workflows/smoke-codex.md @@ -24,9 +24,7 @@ permissions: issues: read pull-requests: read name: Smoke Codex -engine: - id: copilot - copilot-sdk: true +engine: codex imports: - shared/gh.md - shared/reporting-otlp.md @@ -50,21 +48,9 @@ tools: bash: - "*" web-fetch: -lsp: - typescript: - command: typescript-language-server - args: ["--stdio"] - fileExtensions: - ".js": javascript - ".cjs": javascript - ".mjs": javascript - ".ts": typescript - ".tsx": typescriptreact runtimes: go: version: "1.26" - node: - version: "20" safe-outputs: allowed-domains: [default-safe-outputs] add-comment: @@ -138,10 +124,6 @@ features: - date field → `YYYY-MM-DD` - single-select field → an existing option name - If no editable issue fields are available, report this test as skipped with reason -11. **LSP TypeScript Testing**: Use the TypeScript language server (configured via `lsp.typescript` frontmatter) to count the number of functions in `${{ github.workspace }}/actions/setup/js/safe_output_helpers.cjs`: - - Open the file `${{ github.workspace }}/actions/setup/js/safe_output_helpers.cjs` via LSP - - Use LSP document symbols to list all symbols in the file and count functions - - Report the total function count as ✅ if at least 1 function is found, ❌ otherwise ## Output @@ -149,7 +131,7 @@ features: - Title: "Smoke Test: Codex - ${{ github.run_id }}" - Declare `temporary_id: aw_smoke_issue` on this `create_issue` output so that the `set_issue_field` message (test #10) can reference it via `issue_number: '#aw_smoke_issue'` - Body should include: - - Test results (✅ or ❌ for each test, including test #9 Cache Memory, test #10 Set Issue Field, and test #11 LSP TypeScript) + - Test results (✅ or ❌ for each test, including test #9 Cache Memory and test #10 Set Issue Field) - Overall status: PASS or FAIL - Run URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} - Timestamp diff --git a/.github/workflows/smoke-copilot.lock.yml b/.github/workflows/smoke-copilot.lock.yml index 5960f3c1321..c5b0c3b01bc 100644 --- a/.github/workflows/smoke-copilot.lock.yml +++ b/.github/workflows/smoke-copilot.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"75f5b82d2fdf957d6700834c82ba9bdbe38c9961d82211ec32ea40fe3851580d","body_hash":"812899a64607d2d204003410dba1febf488c85700602e8702b89dd7db3609096","strict":true,"agent_id":"copilot","agent_model":"gpt-5.4","engine_versions":{"copilot":"1.0.65"}} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"16ce3056a07e9a41d38b404bbd8dc1ccb2de9962ce637c211ade0ad8977c9af5","body_hash":"05f195a76bd7e9205e793331c9962f6c401e1b29da2e99592ee537c3289df44e","strict":true,"agent_id":"copilot","agent_model":"gpt-5.4","engine_versions":{"copilot":"1.0.65"}} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_GRAFANA_AUTHORIZATION","GH_AW_OTEL_GRAFANA_ENDPOINT","GH_AW_OTEL_SENTRY_AUTHORIZATION","GH_AW_OTEL_SENTRY_ENDPOINT","GITHUB_TOKEN"],"actions":[{"repo":"actions/cache/restore","sha":"27d5ce7f107fe9357f9df03efb73ab90386fccae","version":"v5.0.5"},{"repo":"actions/cache/save","sha":"27d5ce7f107fe9357f9df03efb73ab90386fccae","version":"v5.0.5"},{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7.0.0"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-go","sha":"4a3601121dd01d1626a1e23e37211e3254c1c06c","version":"v6.4.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"docker/build-push-action","sha":"f9f3042f7e2789586610d6e8b85c8f03e5195baf","version":"v7.2.0"},{"repo":"docker/setup-buildx-action","sha":"d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5","version":"v4.1.0"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.11","digest":"sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.11@sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.11","digest":"sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.11@sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d"},{"image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.27.11"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.11","digest":"sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.11@sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.30","digest":"sha256:35625d1a2269b1238606078c879f59a91cffc4ac33eb54bf39c6418822c1a8be","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.30@sha256:35625d1a2269b1238606078c879f59a91cffc4ac33eb54bf39c6418822c1a8be"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.4.0","digest":"sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036","pinned_image":"ghcr.io/github/github-mcp-server:v1.4.0@sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036"},{"image":"ghcr.io/github/serena-mcp-server:latest","digest":"sha256:bf343399e3725c45528f531a230f3a04521d4cdef29f9a5af6282ff0d3c393c5","pinned_image":"ghcr.io/github/serena-mcp-server:latest@sha256:bf343399e3725c45528f531a230f3a04521d4cdef29f9a5af6282ff0d3c393c5"}]} # This file was automatically generated by gh-aw. DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # @@ -654,6 +654,11 @@ jobs: cache: false - name: Capture GOROOT for AWF chroot mode run: echo "GOROOT=$(go env GOROOT)" >> "$GITHUB_ENV" + - name: Setup Node.js + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + with: + node-version: '20' + package-manager-cache: false - name: Create gh-aw temp directory run: bash "${RUNNER_TEMP}/gh-aw/actions/create_gh_aw_tmp_dir.sh" - name: Configure gh CLI for GitHub Enterprise @@ -702,6 +707,11 @@ jobs: GH_HOST: github.com - name: Install AWF binary run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.27.11 + - name: Install TypeScript LSP dependencies + run: npm install -g --ignore-scripts typescript@5.8.3 typescript-language-server@4.3.3 + env: + NPM_CONFIG_MIN_RELEASE_AGE: '3' + timeout-minutes: 10 - name: Install Playwright CLI run: npm install -g @playwright/cli@0.1.14 env: @@ -1864,7 +1874,7 @@ jobs: printf '%s' "$(date +%s%3N)" > /tmp/gh-aw/agent_cli_start_ms.txt trap 'rm -f "$HOME/.copilot/settings.json"' EXIT mkdir -p "$HOME/.copilot" - printf '%s' '{"builtInAgents":{"rubberDuck":false}}' > "$HOME/.copilot/settings.json" + printf '%s' '{"builtInAgents":{"rubberDuck":false},"lspServers":{"typescript":{"command":"typescript-language-server","args":["--stdio"],"fileExtensions":{".cjs":"javascript",".js":"javascript",".mjs":"javascript",".ts":"typescript",".tsx":"typescriptreact"}}}}' > "$HOME/.copilot/settings.json" export XDG_CONFIG_HOME="$HOME" export GH_AW_MCP_CONFIG="$HOME/.copilot/mcp-config.json" touch /tmp/gh-aw/agent-step-summary.md diff --git a/.github/workflows/smoke-copilot.md b/.github/workflows/smoke-copilot.md index bbaec983d5e..3193e2b1bbf 100644 --- a/.github/workflows/smoke-copilot.md +++ b/.github/workflows/smoke-copilot.md @@ -55,9 +55,21 @@ tools: mode: cli web-fetch: cli-proxy: true +lsp: + typescript: + command: typescript-language-server + args: ["--stdio"] + fileExtensions: + ".js": javascript + ".cjs": javascript + ".mjs": javascript + ".ts": typescript + ".tsx": typescriptreact runtimes: go: version: "1.26" + node: + version: "20" models: providers: anthropic: @@ -189,6 +201,10 @@ Run these checks and mark each as ✅/❌: 13. Comment memory: append an original 3-line haiku to `/tmp/gh-aw/comment-memory/*.md`. 14. Sub-agent: use `file-summarizer` on `README.md`. 15. Check run: call `create_check_run` with `conclusion=success`, title `Smoke Copilot - Run ${{ github.run_id }}`, summary `All smoke tests completed.`, text `Detailed results attached.` +16. **LSP TypeScript Testing**: Use the TypeScript language server (configured via `lsp.typescript` frontmatter) to count the number of functions in `${{ github.workspace }}/actions/setup/js/safe_output_helpers.cjs`: + - Open the file `${{ github.workspace }}/actions/setup/js/safe_output_helpers.cjs` via LSP + - Use LSP document symbols to list all symbols in the file and count functions + - Report the total function count as ✅ if at least 1 function is found, ❌ otherwise ## Output @@ -196,7 +212,7 @@ Run these checks and mark each as ✅/❌: - Use the temporary ID `aw_smoke1` for the issue so you can reference it later - Title: "Smoke Test: Copilot - ${{ github.run_id }}" - Body should include: - - Test results (✅ or ❌ for each test) + - Test results (✅ or ❌ for each test, including test #16 LSP TypeScript) - Overall status: PASS or FAIL - Run URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} - Timestamp