WIP: Save agent roles integration work before CHORUS rebrand
- Agent roles and coordination features - Chat API integration testing - New configuration and workspace management 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -10,6 +10,8 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/anthonyrawlins/bzzz/pkg/config"
|
||||
"github.com/anthonyrawlins/bzzz/workspace"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/docker/docker/pkg/stdcopy"
|
||||
@@ -17,11 +19,13 @@ import (
|
||||
|
||||
// Sandbox represents a stateful, isolated execution environment for a single task.
|
||||
type Sandbox struct {
|
||||
ID string // The ID of the running container.
|
||||
HostPath string // The path on the host machine mounted as the workspace.
|
||||
Workspace string // The path inside the container that is the workspace.
|
||||
dockerCli *client.Client
|
||||
ctx context.Context
|
||||
ID string // The ID of the running container.
|
||||
HostPath string // The path on the host machine mounted as the workspace.
|
||||
Workspace string // The path inside the container that is the workspace.
|
||||
dockerCli *client.Client
|
||||
ctx context.Context
|
||||
hcfsWorkspace *workspace.HCFSWorkspace // HCFS-backed workspace
|
||||
workspaceManager *workspace.HCFSWorkspaceManager // HCFS workspace manager
|
||||
}
|
||||
|
||||
// CommandResult holds the output of a command executed in the sandbox.
|
||||
@@ -33,6 +37,11 @@ type CommandResult struct {
|
||||
|
||||
// CreateSandbox provisions a new Docker container for a task.
|
||||
func CreateSandbox(ctx context.Context, taskImage string, agentConfig *config.AgentConfig) (*Sandbox, error) {
|
||||
return CreateSandboxWithHCFS(ctx, taskImage, agentConfig, "", "")
|
||||
}
|
||||
|
||||
// CreateSandboxWithHCFS provisions a new Docker container with HCFS workspace support
|
||||
func CreateSandboxWithHCFS(ctx context.Context, taskImage string, agentConfig *config.AgentConfig, agentID, taskID string) (*Sandbox, error) {
|
||||
if taskImage == "" {
|
||||
taskImage = agentConfig.SandboxImage
|
||||
}
|
||||
@@ -43,10 +52,43 @@ func CreateSandbox(ctx context.Context, taskImage string, agentConfig *config.Ag
|
||||
return nil, fmt.Errorf("failed to create docker client: %w", err)
|
||||
}
|
||||
|
||||
// Create a temporary directory on the host
|
||||
hostPath, err := os.MkdirTemp("", "bzzz-sandbox-")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create temp dir for sandbox: %w", err)
|
||||
// Initialize HCFS workspace manager
|
||||
hcfsAPIURL := os.Getenv("HCFS_API_URL")
|
||||
if hcfsAPIURL == "" {
|
||||
hcfsAPIURL = "http://localhost:8000" // Default HCFS API URL
|
||||
}
|
||||
|
||||
hcfsMountPath := os.Getenv("HCFS_MOUNT_PATH")
|
||||
if hcfsMountPath == "" {
|
||||
hcfsMountPath = "/tmp/hcfs-workspaces" // Default mount path
|
||||
}
|
||||
|
||||
workspaceManager := workspace.NewHCFSWorkspaceManager(hcfsAPIURL, hcfsMountPath)
|
||||
|
||||
var hostPath string
|
||||
var hcfsWorkspace *workspace.HCFSWorkspace
|
||||
|
||||
// Create workspace - use HCFS if agent/task IDs provided, fallback to temp dir
|
||||
if agentID != "" && taskID != "" {
|
||||
// Create HCFS-backed workspace
|
||||
hcfsWorkspace, err = workspaceManager.CreateWorkspace(ctx, agentID, taskID, agentConfig)
|
||||
if err != nil {
|
||||
fmt.Printf("⚠️ Failed to create HCFS workspace, falling back to temp dir: %v\n", err)
|
||||
// Fallback to temporary directory
|
||||
hostPath, err = os.MkdirTemp("", "bzzz-sandbox-")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create temp dir for sandbox: %w", err)
|
||||
}
|
||||
} else {
|
||||
hostPath = workspaceManager.GetWorkspacePath(hcfsWorkspace)
|
||||
}
|
||||
} else {
|
||||
// Create a temporary directory on the host (legacy mode)
|
||||
hostPath, err = os.MkdirTemp("", "bzzz-sandbox-")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create temp dir for sandbox: %w", err)
|
||||
}
|
||||
fmt.Printf("⚠️ Creating sandbox without HCFS (no agent/task ID provided)\n")
|
||||
}
|
||||
|
||||
// Read GitHub token for authentication
|
||||
@@ -97,11 +139,13 @@ func CreateSandbox(ctx context.Context, taskImage string, agentConfig *config.Ag
|
||||
fmt.Printf("✅ Sandbox container %s created successfully.\n", resp.ID[:12])
|
||||
|
||||
return &Sandbox{
|
||||
ID: resp.ID,
|
||||
HostPath: hostPath,
|
||||
Workspace: "/home/agent/work",
|
||||
dockerCli: cli,
|
||||
ctx: ctx,
|
||||
ID: resp.ID,
|
||||
HostPath: hostPath,
|
||||
Workspace: "/home/agent/work",
|
||||
dockerCli: cli,
|
||||
ctx: ctx,
|
||||
hcfsWorkspace: hcfsWorkspace,
|
||||
workspaceManager: workspaceManager,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -128,11 +172,27 @@ func (s *Sandbox) DestroySandbox() error {
|
||||
fmt.Printf("⚠️ Error removing container %s: %v. Proceeding with cleanup.\n", s.ID, err)
|
||||
}
|
||||
|
||||
// Remove the host directory
|
||||
fmt.Printf("🗑️ Removing host directory %s...\n", s.HostPath)
|
||||
err = os.RemoveAll(s.HostPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to remove host directory %s: %w", s.HostPath, err)
|
||||
// Handle workspace cleanup - HCFS vs temp directory
|
||||
if s.hcfsWorkspace != nil && s.workspaceManager != nil {
|
||||
// Store any final artifacts before cleanup
|
||||
artifacts := s.collectWorkspaceArtifacts()
|
||||
if len(artifacts) > 0 {
|
||||
if err := s.workspaceManager.StoreWorkspaceArtifacts(s.ctx, s.hcfsWorkspace, artifacts); err != nil {
|
||||
fmt.Printf("⚠️ Failed to store workspace artifacts: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Destroy HCFS workspace
|
||||
if err := s.workspaceManager.DestroyWorkspace(s.ctx, s.hcfsWorkspace); err != nil {
|
||||
fmt.Printf("⚠️ Failed to destroy HCFS workspace: %v\n", err)
|
||||
}
|
||||
} else {
|
||||
// Legacy mode: Remove the host directory
|
||||
fmt.Printf("🗑️ Removing host directory %s...\n", s.HostPath)
|
||||
err = os.RemoveAll(s.HostPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to remove host directory %s: %w", s.HostPath, err)
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Printf("✅ Sandbox %s destroyed successfully.\n", s.ID[:12])
|
||||
@@ -141,6 +201,11 @@ func (s *Sandbox) DestroySandbox() error {
|
||||
|
||||
// RunCommand executes a shell command inside the sandbox.
|
||||
func (s *Sandbox) RunCommand(command string) (*CommandResult, error) {
|
||||
// Update workspace usage if using HCFS
|
||||
if s.hcfsWorkspace != nil && s.workspaceManager != nil {
|
||||
s.workspaceManager.UpdateWorkspaceUsage(s.hcfsWorkspace)
|
||||
}
|
||||
|
||||
// Configuration for the exec process
|
||||
execConfig := container.ExecOptions{
|
||||
Cmd: []string{"/bin/sh", "-c", command},
|
||||
@@ -258,3 +323,36 @@ func (s *Sandbox) ReadFile(path string) ([]byte, error) {
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// collectWorkspaceArtifacts collects important artifacts from the workspace
|
||||
func (s *Sandbox) collectWorkspaceArtifacts() map[string]string {
|
||||
artifacts := make(map[string]string)
|
||||
|
||||
// Common artifact files to collect
|
||||
artifactFiles := []string{
|
||||
"output/result.txt",
|
||||
"output/summary.md",
|
||||
"logs/execution.log",
|
||||
"build/manifest.json",
|
||||
".bzzz-workspace-state",
|
||||
}
|
||||
|
||||
for _, artifactFile := range artifactFiles {
|
||||
content, err := s.ReadFile(artifactFile)
|
||||
if err == nil && len(content) > 0 {
|
||||
artifacts[artifactFile] = string(content)
|
||||
}
|
||||
}
|
||||
|
||||
return artifacts
|
||||
}
|
||||
|
||||
// GetHCFSWorkspace returns the HCFS workspace if available
|
||||
func (s *Sandbox) GetHCFSWorkspace() *workspace.HCFSWorkspace {
|
||||
return s.hcfsWorkspace
|
||||
}
|
||||
|
||||
// IsUsingHCFS returns true if this sandbox is using HCFS workspace
|
||||
func (s *Sandbox) IsUsingHCFS() bool {
|
||||
return s.hcfsWorkspace != nil && s.workspaceManager != nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user