diff --git a/.github/workflows/glossary-maintainer.lock.yml b/.github/workflows/glossary-maintainer.lock.yml index a420e471f2..f8d60d9050 100644 --- a/.github/workflows/glossary-maintainer.lock.yml +++ b/.github/workflows/glossary-maintainer.lock.yml @@ -26,7 +26,7 @@ # - ../../skills/documentation/SKILL.md # - ../agents/technical-doc-writer.agent.md # -# frontmatter-hash: b26c0b64cc9fd43f7069ca22bdc1735bacf35b24e9bc8ce075abab87e65704bc +# frontmatter-hash: f3292b04834834ce0ed66ae9655de1b9317bcbe277399c396596bae9abbb95ab name: "Glossary Maintainer" "on": diff --git a/.github/workflows/glossary-maintainer.md b/.github/workflows/glossary-maintainer.md index c6feb0c7e7..bde0944fb1 100644 --- a/.github/workflows/glossary-maintainer.md +++ b/.github/workflows/glossary-maintainer.md @@ -15,6 +15,7 @@ permissions: engine: id: copilot + agent: technical-doc-writer network: allowed: diff --git a/.github/workflows/hourly-ci-cleaner.lock.yml b/.github/workflows/hourly-ci-cleaner.lock.yml index 1de1e0a6a6..2ca0ba38e1 100644 --- a/.github/workflows/hourly-ci-cleaner.lock.yml +++ b/.github/workflows/hourly-ci-cleaner.lock.yml @@ -25,7 +25,7 @@ # Imports: # - ../agents/ci-cleaner.agent.md # -# frontmatter-hash: cf34c9ca048904e350b43974103be2dc6b03a96ca98d7459e8ce4c281e8d185e +# frontmatter-hash: c2015369cb8f8feeba5f0da4bc882b76e3cb901c145b73a1ae1deb95433c207d name: "CI Cleaner" "on": diff --git a/.github/workflows/hourly-ci-cleaner.md b/.github/workflows/hourly-ci-cleaner.md index deac777f02..27417ad0dc 100644 --- a/.github/workflows/hourly-ci-cleaner.md +++ b/.github/workflows/hourly-ci-cleaner.md @@ -16,7 +16,9 @@ tracker-id: hourly-ci-cleaner # - Target: Focus on systematic fix application with minimal iteration # - Budget target: 15-20 turns for typical CI fixes # Note: max-turns not available for Copilot engine (Claude only) -engine: copilot +engine: + id: copilot + agent: ci-cleaner network: allowed: - defaults diff --git a/.github/workflows/smoke-claude-tmp.lock.yml b/.github/workflows/smoke-claude-tmp.lock.yml deleted file mode 100644 index 352a0f0b8d..0000000000 --- a/.github/workflows/smoke-claude-tmp.lock.yml +++ /dev/null @@ -1,34 +0,0 @@ - -name: "AAA Smoke Claude" -"on": - workflow_dispatch: null - -permissions: {} - -jobs: - pre_activation: - # a condition that is always false to test activation - if: false - runs-on: ubuntu-slim - steps: - - run: | - echo "pre activation" - outputs: - activated: ${{ 'true' }} - - activation: - needs: pre_activation - if: always() && !cancelled() && (needs.pre_activation.result == 'skipped' || needs.pre_activation.outputs.activated == 'true') - runs-on: ubuntu-slim - steps: - - run: | - echo "activation, needs.pre_activation.result=${{ needs.pre_activation.result }}" - - agent: - needs: ["pre_activation", "activation"] - if: always() && !cancelled() && (needs.pre_activation.result == 'skipped' || needs.pre_activation.outputs.activated == 'true') - runs-on: ubuntu-slim - steps: - - run: | - echo "agent" - diff --git a/.github/workflows/technical-doc-writer.lock.yml b/.github/workflows/technical-doc-writer.lock.yml index e7c14d6185..c18069bf9b 100644 --- a/.github/workflows/technical-doc-writer.lock.yml +++ b/.github/workflows/technical-doc-writer.lock.yml @@ -26,7 +26,7 @@ # - ../../skills/documentation/SKILL.md # - ../agents/technical-doc-writer.agent.md # -# frontmatter-hash: ff7a028a92576ae9c1fb329f2dbf2c65c560924785a632bf513fc5db1b71eb28 +# frontmatter-hash: e103eb4f49396291813515fc7d3dc89b29868a12f2c4786865c2028ad711fab0 name: "Rebuild the documentation after making changes" "on": diff --git a/.github/workflows/technical-doc-writer.md b/.github/workflows/technical-doc-writer.md index 2ad988d954..44cff34ef6 100644 --- a/.github/workflows/technical-doc-writer.md +++ b/.github/workflows/technical-doc-writer.md @@ -16,6 +16,7 @@ permissions: engine: id: copilot + agent: technical-doc-writer network: allowed: diff --git a/docs/src/content/docs/reference/frontmatter-full.md b/docs/src/content/docs/reference/frontmatter-full.md index 41715a859b..74d3fcaca7 100644 --- a/docs/src/content/docs/reference/frontmatter-full.md +++ b/docs/src/content/docs/reference/frontmatter-full.md @@ -1228,6 +1228,11 @@ engine: # (optional) config: "example-value" + # Agent identifier to pass to copilot --agent flag (copilot engine only). + # Specifies which custom agent to use for the workflow. + # (optional) + agent: "example-value" + # Optional array of command-line arguments to pass to the AI engine CLI. These # arguments are injected after all other args but before the prompt. # (optional) diff --git a/pkg/parser/schemas/main_workflow_schema.json b/pkg/parser/schemas/main_workflow_schema.json index 45fedd88d1..b620458f66 100644 --- a/pkg/parser/schemas/main_workflow_schema.json +++ b/pkg/parser/schemas/main_workflow_schema.json @@ -6752,6 +6752,10 @@ "type": "string", "description": "Additional TOML configuration text that will be appended to the generated config.toml in the action (codex engine only)" }, + "agent": { + "type": "string", + "description": "Agent identifier to pass to copilot --agent flag (copilot engine only). Specifies which custom agent to use for the workflow." + }, "args": { "type": "array", "items": { diff --git a/pkg/workflow/copilot_engine_execution.go b/pkg/workflow/copilot_engine_execution.go index 9ddc30643f..0bd1a093b5 100644 --- a/pkg/workflow/copilot_engine_execution.go +++ b/pkg/workflow/copilot_engine_execution.go @@ -73,11 +73,12 @@ func (e *CopilotEngine) GetExecutionSteps(workflowData *WorkflowData, logFile st copilotArgs = append(copilotArgs, "--model", workflowData.EngineConfig.Model) } - // Add --agent flag if custom agent file is specified (via imports) - // Copilot CLI expects agent identifier (filename without extension), not full path - if workflowData.AgentFile != "" { - agentIdentifier := ExtractAgentIdentifier(workflowData.AgentFile) - copilotExecLog.Printf("Using custom agent: %s (identifier: %s)", workflowData.AgentFile, agentIdentifier) + // Add --agent flag if specified via engine.agent + // Note: Agent imports (.github/agents/*.md) still work for importing markdown content, + // but they do NOT automatically set the --agent flag. Only engine.agent controls the flag. + if workflowData.EngineConfig != nil && workflowData.EngineConfig.Agent != "" { + agentIdentifier := workflowData.EngineConfig.Agent + copilotExecLog.Printf("Using agent from engine.agent: %s", agentIdentifier) copilotArgs = append(copilotArgs, "--agent", agentIdentifier) } diff --git a/pkg/workflow/engine.go b/pkg/workflow/engine.go index 73fb222d58..1822c798f4 100644 --- a/pkg/workflow/engine.go +++ b/pkg/workflow/engine.go @@ -24,6 +24,7 @@ type EngineConfig struct { Config string Args []string Firewall *FirewallConfig // AWF firewall configuration + Agent string // Agent identifier for copilot --agent flag (copilot engine only) } // NetworkPermissions represents network access permissions for workflow execution @@ -200,6 +201,14 @@ func (c *Compiler) ExtractEngineConfig(frontmatter map[string]any) (string, *Eng } } + // Extract optional 'agent' field (string - copilot engine only) + if agent, hasAgent := engineObj["agent"]; hasAgent { + if agentStr, ok := agent.(string); ok { + config.Agent = agentStr + engineLog.Printf("Extracted agent identifier: %s", agentStr) + } + } + // Extract optional 'firewall' field (object format) if firewall, hasFirewall := engineObj["firewall"]; hasFirewall { if firewallObj, ok := firewall.(map[string]any); ok { diff --git a/pkg/workflow/engine_agent_import_test.go b/pkg/workflow/engine_agent_import_test.go index e3f29fac9a..b0870d56c5 100644 --- a/pkg/workflow/engine_agent_import_test.go +++ b/pkg/workflow/engine_agent_import_test.go @@ -9,7 +9,33 @@ import ( "testing" ) -// TestCopilotEngineWithAgentFromImports tests that copilot engine includes --agent flag when agent file is imported +// TestCopilotEngineWithAgentFromEngineConfig tests that copilot engine includes --agent flag when specified in engine.agent +func TestCopilotEngineWithAgentFromEngineConfig(t *testing.T) { + engine := NewCopilotEngine() + workflowData := &WorkflowData{ + Name: "test-workflow", + EngineConfig: &EngineConfig{ + ID: "copilot", + Agent: "my-custom-agent", + }, + } + + 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") + + // Copilot CLI expects agent identifier + if !strings.Contains(stepContent, `--agent my-custom-agent`) { + t.Errorf("Expected '--agent my-custom-agent' in copilot command, got:\n%s", stepContent) + } +} + +// TestCopilotEngineWithAgentFromImports tests that agent imports do NOT set --agent flag +// Agent imports only import markdown content, not agent configuration func TestCopilotEngineWithAgentFromImports(t *testing.T) { engine := NewCopilotEngine() workflowData := &WorkflowData{ @@ -28,9 +54,39 @@ func TestCopilotEngineWithAgentFromImports(t *testing.T) { stepContent := strings.Join([]string(steps[0]), "\n") - // Copilot CLI expects agent identifier (filename without extension), not full path - if !strings.Contains(stepContent, `--agent test-agent`) { - t.Errorf("Expected '--agent test-agent' in copilot command, got:\n%s", stepContent) + // Agent imports should NOT set --agent flag (only engine.agent does) + if strings.Contains(stepContent, `--agent`) { + t.Errorf("Did not expect '--agent' flag when only AgentFile is set (without engine.agent), got:\n%s", stepContent) + } +} + +// TestCopilotEngineAgentOnlyFromEngineConfig tests that --agent flag is only set by engine.agent +func TestCopilotEngineAgentOnlyFromEngineConfig(t *testing.T) { + engine := NewCopilotEngine() + workflowData := &WorkflowData{ + Name: "test-workflow", + EngineConfig: &EngineConfig{ + ID: "copilot", + Agent: "explicit-agent", + }, + AgentFile: ".github/agents/import-agent.md", + } + + 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") + + // Should only use explicit agent from engine.agent + if !strings.Contains(stepContent, `--agent explicit-agent`) { + t.Errorf("Expected '--agent explicit-agent' in copilot command, got:\n%s", stepContent) + } + // Should not use agent from imports + if strings.Contains(stepContent, `--agent import-agent`) { + t.Errorf("Did not expect '--agent import-agent' when engine.agent is set, got:\n%s", stepContent) } }