Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions .github/workflows/dev.lock.yml

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

4 changes: 2 additions & 2 deletions .github/workflows/issue-classifier.lock.yml

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

6 changes: 4 additions & 2 deletions .github/workflows/smoke-copilot-sdk.lock.yml

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

4 changes: 2 additions & 2 deletions .github/workflows/smoke-opencode.lock.yml

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

32 changes: 7 additions & 25 deletions pkg/cli/workflows/test-custom-agent.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ network:
- example.com
firewall: true

# Custom Agent Configuration (works for both AWF and SRT)
# Custom Agent Configuration (works for AWF)
# This example shows how to use a custom command to replace the standard AWF binary
sandbox:
agent:
id: awf # Agent identifier (awf or srt)
id: awf # Agent identifier (awf)
command: "docker run --rm -it my-custom-awf-image" # Custom command replaces AWF binary download
args:
- "--custom-logging" # Additional arguments appended to AWF command
Expand All @@ -33,9 +33,9 @@ tools:

# Custom Agent Configuration Example

This workflow demonstrates the custom agent configuration capabilities that work for **both AWF and SRT**:
This workflow demonstrates the custom agent configuration capabilities for **AWF** (Agent Workflow Firewall):

1. **Custom Command**: Replace the standard AWF or SRT installation with any command (e.g., Docker container, custom script)
1. **Custom Command**: Replace the standard AWF installation with any command (e.g., Docker container, custom script)
2. **Custom Args**: Add additional arguments that are appended to the command
3. **Custom Env**: Set environment variables on the execution step

Expand All @@ -47,12 +47,6 @@ This workflow demonstrates the custom agent configuration capabilities that work
- **Testing**: Use a modified AWF binary for testing new features
- **Debugging**: Add debug flags and environment variables for troubleshooting

### For SRT (Sandbox Runtime)
- **Custom SRT Wrapper**: Use a custom wrapper around the Anthropic Sandbox Runtime
- **Pre-configured Container**: Run SRT from a Docker image with custom settings
- **Custom Isolation**: Implement custom sandboxing logic that wraps SRT
- **Testing & Development**: Use a modified SRT setup for testing

## Example Configurations

### AWF with Custom Command
Expand All @@ -67,24 +61,10 @@ sandbox:
AWF_LOG_LEVEL: "debug"
```

### SRT with Custom Command

```yaml
features:
sandbox-runtime: true
sandbox:
agent:
id: srt
command: "custom-srt-wrapper"
args: ["--custom-arg"]
env:
SRT_DEBUG: "true"
```

## Configuration Reference

The `sandbox.agent` object supports:
- `id`: Agent identifier ("awf" or "srt")
- `id`: Agent identifier ("awf")
- `command`: Custom command to replace the default installation (optional)
- `args`: Array of additional arguments to append (optional)
- `env`: Object with environment variables to set (optional)
Expand All @@ -101,4 +81,6 @@ sandbox:
type: awf # Still works!
```

Legacy `srt` and `sandbox-runtime` values are automatically migrated to `awf`.

Review the changes in this pull request.
10 changes: 5 additions & 5 deletions pkg/parser/schemas/main_workflow_schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -2229,21 +2229,21 @@
]
},
"sandbox": {
"description": "Sandbox configuration for AI engines. Controls agent sandbox (AWF or Sandbox Runtime) and MCP gateway. The MCP gateway is always enabled and cannot be disabled.",
"description": "Sandbox configuration for AI engines. Controls agent sandbox (AWF) and MCP gateway. The MCP gateway is always enabled and cannot be disabled.",
"oneOf": [
{
"type": "string",
"enum": ["default", "awf", "srt", "sandbox-runtime"],
"description": "Legacy string format for sandbox type: 'default' for no sandbox, 'awf' for Agent Workflow Firewall, 'srt' and 'sandbox-runtime' are deprecated and will be migrated to 'awf'"
"enum": ["default", "awf"],
"description": "String format for sandbox type: 'default' for no sandbox, 'awf' for Agent Workflow Firewall. Note: Legacy 'srt' and 'sandbox-runtime' values are automatically migrated to 'awf'"
},
{
"type": "object",
"description": "Object format for full sandbox configuration with agent and mcp options",
"properties": {
"type": {
"type": "string",
"enum": ["default", "awf", "srt", "sandbox-runtime"],
"description": "Legacy sandbox type field (use agent instead). 'srt' and 'sandbox-runtime' are deprecated and will be migrated to 'awf'"
"enum": ["default", "awf"],
"description": "Legacy sandbox type field (use agent instead). Note: Legacy 'srt' and 'sandbox-runtime' values are automatically migrated to 'awf'"
},
"agent": {
"description": "Agent sandbox type: 'awf' uses AWF (Agent Workflow Firewall), or false to disable agent sandbox. Defaults to 'awf' if not specified. Note: Disabling the agent sandbox (false) removes firewall protection but keeps the MCP gateway enabled.",
Expand Down
5 changes: 2 additions & 3 deletions pkg/workflow/compiler_safe_outputs.go
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ func (c *Compiler) applyDefaultTools(tools map[string]any, safeOutputs *SafeOutp

// Enable edit and bash tools by default when sandbox is enabled
// The sandbox is enabled when:
// 1. Explicitly configured via sandbox.agent (awf/srt)
// 1. Explicitly configured via sandbox.agent (awf)
// 2. Auto-enabled by firewall default enablement (when network restrictions are present)
if isSandboxEnabled(sandboxConfig, networkPermissions) {
compilerSafeOutputsLog.Print("Sandbox enabled, applying default edit and bash tools")
Expand Down Expand Up @@ -467,9 +467,8 @@ func needsGitCommands(safeOutputs *SafeOutputsConfig) bool {

// isSandboxEnabled checks if the sandbox is enabled (either explicitly or auto-enabled)
// Returns true when:
// - sandbox.agent is explicitly set to a sandbox type (awf, srt, etc.)
// - sandbox.agent is explicitly set to awf
// - Firewall is auto-enabled (networkPermissions.Firewall is set and enabled)
// - SRT sandbox is enabled
// Returns false when:
// - sandbox.agent is false (explicitly disabled)
// - No sandbox configuration and no auto-enabled firewall
Expand Down
1 change: 0 additions & 1 deletion pkg/workflow/copilot_engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
// - copilot_engine_tools.go: Tool permissions, arguments, and error patterns
// - copilot_logs.go: Log parsing, metrics extraction, and log management
// - copilot_mcp.go: MCP server configuration rendering
// - copilot_srt.go: Sandbox Runtime (SRT) integration
// - copilot_participant_steps.go: Copilot CLI participant steps
//
// This modular organization improves maintainability and makes it easier
Expand Down
4 changes: 2 additions & 2 deletions pkg/workflow/frontmatter_extraction_security.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ func (c *Compiler) extractSandboxConfig(frontmatter map[string]any) *SandboxConf
return nil
}

// Handle legacy string format: "default" or "sandbox-runtime"
// Handle legacy string format: "default" or "awf" (legacy srt/sandbox-runtime are auto-migrated)
if sandboxStr, ok := sandbox.(string); ok {
frontmatterExtractionSecurityLog.Printf("Sandbox string format: type=%s", sandboxStr)
sandboxType := SandboxType(sandboxStr)
Expand Down Expand Up @@ -239,7 +239,7 @@ func (c *Compiler) extractAgentSandboxConfig(agentVal any) *AgentSandboxConfig {
return nil
}

// Handle string format: "awf" or "srt"
// Handle string format: "awf" or false (legacy srt values are auto-migrated)
if agentStr, ok := agentVal.(string); ok {
agentType := SandboxType(agentStr)
if isSupportedSandboxType(agentType) {
Expand Down
20 changes: 9 additions & 11 deletions pkg/workflow/sandbox_agent_tools_default_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,23 +53,21 @@ Test workflow to verify sandbox.agent: awf enables edit and bash tools.
assert.Contains(t, lockStr, "bash", "Expected bash tool to be enabled by default with sandbox.agent: awf")
})

t.Run("sandbox.agent: srt enables edit and bash tools by default", func(t *testing.T) {
t.Run("sandbox.agent: awf (migrated from srt) enables edit and bash tools by default", func(t *testing.T) {
// Create temp directory for test workflows
workflowsDir := t.TempDir()

markdown := `---
engine: copilot
sandbox:
agent: srt
features:
sandbox-runtime: true
agent: awf
on: workflow_dispatch
---

Test workflow to verify sandbox.agent: srt enables edit and bash tools.
Test workflow to verify sandbox.agent: awf enables edit and bash tools.
`

workflowPath := filepath.Join(workflowsDir, "test-agent-srt.md")
workflowPath := filepath.Join(workflowsDir, "test-agent-awf.md")
err := os.WriteFile(workflowPath, []byte(markdown), 0644)
require.NoError(t, err, "Failed to write workflow file")

Expand All @@ -81,17 +79,17 @@ Test workflow to verify sandbox.agent: srt enables edit and bash tools.
require.NoError(t, err, "Compilation failed")

// Read the compiled workflow
lockPath := filepath.Join(workflowsDir, "test-agent-srt.lock.yml")
lockPath := filepath.Join(workflowsDir, "test-agent-awf.lock.yml")
lockContent, err := os.ReadFile(lockPath)
require.NoError(t, err, "Failed to read compiled workflow")

lockStr := string(lockContent)

// Verify that edit tool is present
assert.Contains(t, lockStr, "edit", "Expected edit tool to be enabled by default with sandbox.agent: srt")
assert.Contains(t, lockStr, "edit", "Expected edit tool to be enabled by default with sandbox.agent: awf")

// Verify that bash tool is present
assert.Contains(t, lockStr, "bash", "Expected bash tool to be enabled by default with sandbox.agent: srt")
assert.Contains(t, lockStr, "bash", "Expected bash tool to be enabled by default with sandbox.agent: awf")
})

t.Run("default sandbox (awf) does NOT enable edit and bash tools", func(t *testing.T) {
Expand Down Expand Up @@ -308,7 +306,7 @@ func TestIsSandboxEnabled(t *testing.T) {
expected: true,
},
{
name: "agent srt explicitly configured",
name: "agent awf explicitly configured",
sandboxConfig: &SandboxConfig{
Agent: &AgentSandboxConfig{
Type: SandboxTypeAWF,
Expand Down Expand Up @@ -338,7 +336,7 @@ func TestIsSandboxEnabled(t *testing.T) {
expected: true,
},
{
name: "agent with ID srt",
name: "agent with ID awf (new format)",
sandboxConfig: &SandboxConfig{
Agent: &AgentSandboxConfig{
ID: "awf",
Expand Down
38 changes: 18 additions & 20 deletions pkg/workflow/sandbox_custom_agent_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -248,9 +248,9 @@ sandbox:
}
})

t.Run("custom command and args for SRT", func(t *testing.T) {
t.Run("custom command and args for AWF", func(t *testing.T) {
// Create temp directory for test
tmpDir, err := os.MkdirTemp("", "custom-srt-test")
tmpDir, err := os.MkdirTemp("", "custom-awf-test")
if err != nil {
t.Fatal(err)
}
Expand All @@ -260,21 +260,19 @@ sandbox:
on:
workflow_dispatch:
engine: copilot
features:
sandbox-runtime: true
sandbox:
agent:
id: awf
command: "custom-srt-wrapper"
command: "custom-awf-wrapper"
args:
- "--custom-srt-arg"
- "--custom-awf-arg"
- "--debug"
env:
SRT_CUSTOM_VAR: "test_value"
SRT_DEBUG: "true"
AWF_CUSTOM_VAR: "test_value"
AWF_DEBUG: "true"
---

# Test Custom SRT
# Test Custom AWF
`

testFile := filepath.Join(tmpDir, "test-workflow.md")
Expand All @@ -300,30 +298,30 @@ sandbox:
}
lockStr := string(lockContent)

// Verify custom SRT command is used
if !strings.Contains(lockStr, "custom-srt-wrapper") {
t.Error("Expected custom SRT command 'custom-srt-wrapper' in compiled workflow")
// Verify custom AWF command is used
if !strings.Contains(lockStr, "custom-awf-wrapper") {
t.Error("Expected custom AWF command 'custom-awf-wrapper' in compiled workflow")
}

// Verify custom args are included
if !strings.Contains(lockStr, "--custom-srt-arg") {
t.Error("Expected custom arg '--custom-srt-arg' in compiled workflow")
if !strings.Contains(lockStr, "--custom-awf-arg") {
t.Error("Expected custom arg '--custom-awf-arg' in compiled workflow")
}
if !strings.Contains(lockStr, "--debug") {
t.Error("Expected custom arg '--debug' in compiled workflow")
}

// Verify custom env is included
if !strings.Contains(lockStr, "SRT_CUSTOM_VAR: test_value") {
t.Error("Expected custom env 'SRT_CUSTOM_VAR: test_value' in compiled workflow")
if !strings.Contains(lockStr, "AWF_CUSTOM_VAR: test_value") {
t.Error("Expected custom env 'AWF_CUSTOM_VAR: test_value' in compiled workflow")
}
if !strings.Contains(lockStr, "SRT_DEBUG: true") {
t.Error("Expected custom env 'SRT_DEBUG: true' in compiled workflow")
if !strings.Contains(lockStr, "AWF_DEBUG: true") {
t.Error("Expected custom env 'AWF_DEBUG: true' in compiled workflow")
}

// Verify installation steps were skipped
if strings.Contains(lockStr, "Install Sandbox Runtime") {
t.Error("Expected SRT installation step to be skipped when custom command is specified")
if strings.Contains(lockStr, "Install AWF") {
t.Error("Expected AWF installation step to be skipped when custom command is specified")
}
})
}
Loading
Loading