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
8 changes: 6 additions & 2 deletions pkg/workflow/cloud_hypervisor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -205,8 +205,11 @@ func TestCloudHypervisorAWFConfigJSON(t *testing.T) {
NetworkPermissions: &NetworkPermissions{
Firewall: &FirewallConfig{Enabled: true},
},
Tools: map[string]any{"github": map[string]any{"mode": "gh-proxy"}},
SandboxConfig: &SandboxConfig{Agent: &AgentSandboxConfig{ID: "awf", Runtime: AgentRuntimeCloudHypervisor}},
Tools: map[string]any{"github": map[string]any{"mode": "gh-proxy"}},
SandboxConfig: applySandboxDefaults(
&SandboxConfig{Agent: &AgentSandboxConfig{ID: "awf", Runtime: AgentRuntimeCloudHypervisor}},
&EngineConfig{ID: "copilot"},
),
},
}

Expand All @@ -218,6 +221,7 @@ func TestCloudHypervisorAWFConfigJSON(t *testing.T) {
assert.Contains(t, jsonStr, `"topologyAttach":["awmg-mcpg"]`)
assert.NotContains(t, jsonStr, "awmg-cli-proxy")
assert.Contains(t, jsonStr, `"agentTimeout":60`)
assert.Contains(t, jsonStr, `"allowWrite":["/tmp/gh-aw/agent","/tmp/gh-aw/sandbox/agent/logs","/workspace","/workspace/.awf-home"]`)
}

func TestCloudHypervisorValidationArcDindIncompatible(t *testing.T) {
Expand Down
23 changes: 15 additions & 8 deletions pkg/workflow/sandbox.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package workflow
import (
"slices"

"github.com/github/gh-aw/pkg/constants"
"github.com/github/gh-aw/pkg/logger"
"github.com/github/gh-aw/pkg/sliceutil"
)
Expand All @@ -30,7 +31,10 @@ const (
SandboxTypeDefault SandboxType = "default" // Alias for AWF (backward compat)
)

const defaultAgentWorkspaceWritePath = "/tmp/gh-aw/agent"
const (
defaultAgentWorkspaceWritePath = "/tmp/gh-aw/agent"
defaultAgentLogsWritePath = "/tmp/gh-aw/sandbox/agent/logs"
)

// SandboxConfig represents the top-level sandbox configuration from front matter
// New format: { agent: "awf"|"srt"|{type, config}, mcp: {port, command, ...} }
Expand Down Expand Up @@ -246,15 +250,15 @@ func applySandboxDefaults(sandboxConfig *SandboxConfig, engineConfig *EngineConf
Type: SandboxTypeAWF,
},
}
ensureDefaultAgentWritePath(sandboxConfig)
ensureDefaultAgentWritePath(sandboxConfig, engineConfig)
return sandboxConfig
}

// If sandbox config exists with legacy Type field set, don't override with awf default
// The legacy Type field indicates explicit sandbox configuration
if sandboxConfig.Type != "" {
sandboxLog.Printf("Sandbox config uses legacy Type field: %s, preserving it", sandboxConfig.Type)
ensureDefaultAgentWritePath(sandboxConfig)
ensureDefaultAgentWritePath(sandboxConfig, engineConfig)
return sandboxConfig
}

Expand All @@ -264,7 +268,7 @@ func applySandboxDefaults(sandboxConfig *SandboxConfig, engineConfig *EngineConf
sandboxConfig.Agent = &AgentSandboxConfig{
Type: SandboxTypeAWF,
}
ensureDefaultAgentWritePath(sandboxConfig)
ensureDefaultAgentWritePath(sandboxConfig, engineConfig)
return sandboxConfig
}

Expand All @@ -279,12 +283,12 @@ func applySandboxDefaults(sandboxConfig *SandboxConfig, engineConfig *EngineConf
sandboxConfig.Agent.Type = SandboxTypeAWF
}

ensureDefaultAgentWritePath(sandboxConfig)
ensureDefaultAgentWritePath(sandboxConfig, engineConfig)
return sandboxConfig
}

// cloudHypervisorWorkspaceWritePath and cloudHypervisorAwfHomeWritePath are the additional
// filesystem.allowWrite entries seeded for the Cloud Hypervisor runtime.
// Cloud Hypervisor requires explicit filesystem.allowWrite entries for compiler-managed
// output paths as well as the workspace.
//
// Under Cloud Hypervisor, /workspace and /tmp/gh-aw are separate virtiofs exports, and the
// AWF planner narrows each export independently based on the allowWrite entries that fall
Expand All @@ -306,7 +310,7 @@ const cloudHypervisorAwfHomeWritePath = "/workspace/.awf-home"
// container fails to start ("make mountpoint \"/tmp/awf-init\": read-only file system").
// Seeding a default there would therefore break every compose-runtime workflow, so
// filesystem.allowWrite stays opt-in for those runtimes.
func ensureDefaultAgentWritePath(sandboxConfig *SandboxConfig) {
func ensureDefaultAgentWritePath(sandboxConfig *SandboxConfig, engineConfig *EngineConfig) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pkg/workflow/sandbox.go:312: yagni: the new engineConfig plumbing for a one-off copilot branch. Keep the helper engine-agnostic and add the log-path tweak inline at the call site until a second engine needs it.

if sandboxConfig == nil || sandboxConfig.Agent == nil {
return
}
Expand All @@ -320,6 +324,9 @@ func ensureDefaultAgentWritePath(sandboxConfig *SandboxConfig) {
sandboxConfig.Agent.Config.Filesystem = &SRTFilesystemConfig{}
}
addAllowWritePathIfMissing(sandboxConfig.Agent.Config.Filesystem, defaultAgentWorkspaceWritePath)
if engineConfig != nil && engineConfig.ID == string(constants.CopilotEngine) {
addAllowWritePathIfMissing(sandboxConfig.Agent.Config.Filesystem, defaultAgentLogsWritePath)
}
addAllowWritePathIfMissing(sandboxConfig.Agent.Config.Filesystem, cloudHypervisorWorkspaceWritePath)
addAllowWritePathIfMissing(sandboxConfig.Agent.Config.Filesystem, cloudHypervisorAwfHomeWritePath)
}
Expand Down
26 changes: 25 additions & 1 deletion pkg/workflow/sandbox_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ func TestApplySandboxDefaults(t *testing.T) {
expected *SandboxConfig
expectDefaultWritePath bool
expectedAllowWrite []string
unexpectedAllowWrite []string
}{
{
name: "nil config creates default with AWF",
Expand Down Expand Up @@ -245,7 +246,7 @@ func TestApplySandboxDefaults(t *testing.T) {
// Cloud Hypervisor narrows the /workspace and /tmp/gh-aw exports independently,
// so the default write path alone would leave /workspace (and the CH-managed
// HOME under it) read-only. See ensureDefaultAgentWritePath.
name: "cloud-hypervisor runtime seeds agent, workspace and awf-home write paths",
name: "cloud-hypervisor runtime seeds agent, logs, workspace and awf-home write paths",
config: &SandboxConfig{
Agent: &AgentSandboxConfig{
Type: SandboxTypeAWF,
Expand All @@ -254,7 +255,25 @@ func TestApplySandboxDefaults(t *testing.T) {
},
engine: &EngineConfig{ID: "copilot"},
expectDefaultWritePath: true,
expectedAllowWrite: []string{defaultAgentWorkspaceWritePath, defaultAgentLogsWritePath, cloudHypervisorWorkspaceWritePath, cloudHypervisorAwfHomeWritePath},
expected: &SandboxConfig{
Agent: &AgentSandboxConfig{
Type: SandboxTypeAWF,
},
},
},
{
name: "cloud-hypervisor runtime does not grant Copilot logs path to other engines",
config: &SandboxConfig{
Agent: &AgentSandboxConfig{
Type: SandboxTypeAWF,
Runtime: AgentRuntimeCloudHypervisor,
},
},
engine: &EngineConfig{ID: "claude"},
expectDefaultWritePath: true,
expectedAllowWrite: []string{defaultAgentWorkspaceWritePath, cloudHypervisorWorkspaceWritePath, cloudHypervisorAwfHomeWritePath},
unexpectedAllowWrite: []string{defaultAgentLogsWritePath},
expected: &SandboxConfig{
Agent: &AgentSandboxConfig{
Type: SandboxTypeAWF,
Expand Down Expand Up @@ -285,6 +304,11 @@ func TestApplySandboxDefaults(t *testing.T) {
require.NotNil(t, result.Agent.Config.Filesystem)
assert.Contains(t, result.Agent.Config.Filesystem.AllowWrite, expectedPath)
}
for _, unexpectedPath := range tt.unexpectedAllowWrite {
require.NotNil(t, result.Agent.Config)
require.NotNil(t, result.Agent.Config.Filesystem)
assert.NotContains(t, result.Agent.Config.Filesystem.AllowWrite, unexpectedPath)
}
})
}
}
Expand Down
Loading