From d0e3ff2cd8be51f7d8aa8cf7aa52e842ff9ea541 Mon Sep 17 00:00:00 2001 From: Levi Neely Date: Wed, 4 Mar 2026 13:10:10 +0100 Subject: [PATCH] Rename role to sandbox in layered sandbox config MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - cfg/roles/ → cfg/sandbox/ - DefaultRole → DefaultSandbox, LoadRole → LoadSandbox - SessionOptions.Role → Sandbox, Session.Role() → Sandbox() - p9 file: role → sandbox - Remove tasks from layered sandbox config - Update create_session.sh and SKILL.md docs --- cfg/{roles => sandbox}/default.yaml | 2 ++ internal/backend/backend.go | 14 +++++-------- internal/backend/tmux/session.go | 30 ++++++++++------------------ internal/backend/tmux/tmux.go | 19 +++++++++--------- internal/p9/server.go | 31 ++++++++++------------------- internal/sandbox/config.go | 14 ++++++------- mcptools/create_session.sh | 4 ++-- skills/anvillm-sessions/SKILL.md | 4 ++-- 8 files changed, 49 insertions(+), 69 deletions(-) rename cfg/{roles => sandbox}/default.yaml (98%) diff --git a/cfg/roles/default.yaml b/cfg/sandbox/default.yaml similarity index 98% rename from cfg/roles/default.yaml rename to cfg/sandbox/default.yaml index 0fbdebd..9dc2143 100644 --- a/cfg/roles/default.yaml +++ b/cfg/sandbox/default.yaml @@ -107,5 +107,7 @@ env: - BITBUCKET_TOKEN - BITBUCKET_USERNAME - PCLOUD_USER + - EDITOR + - GIT_SEQUENCE_EDITOR network: unrestricted: true diff --git a/internal/backend/backend.go b/internal/backend/backend.go index 43cfc93..67a5cc3 100644 --- a/internal/backend/backend.go +++ b/internal/backend/backend.go @@ -15,10 +15,9 @@ var ( // SessionOptions contains options for creating a session type SessionOptions struct { - CWD string - Role string - Tasks []string - Model string // Optional model override (empty = backend default) + CWD string + Sandbox string + Model string // Optional model override (empty = backend default) } // Backend represents any chat backend (CLI tool via PTY, or direct API) @@ -74,11 +73,8 @@ type Session interface { // SetAlias sets a user-friendly alias for the session SetAlias(alias string) - // Role returns the session role (empty if not specified) - Role() string - - // Tasks returns the session tasks (empty if not specified) - Tasks() []string + // Sandbox returns the session sandbox config (empty if not specified) + Sandbox() string // Model returns the active model override (empty = backend default) Model() string diff --git a/internal/backend/tmux/session.go b/internal/backend/tmux/session.go index 33e6fb0..c038fbf 100644 --- a/internal/backend/tmux/session.go +++ b/internal/backend/tmux/session.go @@ -26,9 +26,8 @@ type Session struct { windowName string // window name within tmux session (same as id) cwd string alias string - role string // session role (e.g., "developer", "reviewer") - tasks []string // session tasks (e.g., ["git", "docker"]) - winID int // Acme window ID (0 if not applicable) + sandbox string // sandbox config (e.g., "default", "restricted") + winID int // Acme window ID (0 if not applicable) pid int state string context string // injected into first prompt only (consumed on first Send) @@ -189,18 +188,11 @@ func (s *Session) Commands() backend.CommandHandler { return s.commands } -// Role returns the session role -func (s *Session) Role() string { +// Sandbox returns the session sandbox config +func (s *Session) Sandbox() string { s.mu.Lock() defer s.mu.Unlock() - return s.role -} - -// Tasks returns the session tasks -func (s *Session) Tasks() []string { - s.mu.Lock() - defer s.mu.Unlock() - return s.tasks + return s.sandbox } // Model returns the active model override (empty = backend default) @@ -670,7 +662,7 @@ func (s *Session) Restart(ctx context.Context) error { environment := s.environment backendName := s.backendName backendCommand := s.backendCommand - role := s.role + sbx := s.sandbox hadCrash := s.hadCrash model := s.model modelResolver := s.modelResolver @@ -711,14 +703,14 @@ func (s *Session) Restart(ctx context.Context) error { } layers = append(layers, backendLayer) - if role == "" { - role = sandbox.DefaultRole + if sbx == "" { + sbx = sandbox.DefaultSandbox } - roleLayer, err := sandbox.LoadRole(role) + sbxLayer, err := sandbox.LoadSandbox(sbx) if err != nil { - return fmt.Errorf("failed to load role %q: %w", role, err) + return fmt.Errorf("failed to load sandbox %q: %w", sbx, err) } - layers = append(layers, roleLayer) + layers = append(layers, sbxLayer) general := sandbox.GeneralConfig{BestEffort: false, LogLevel: "error"} advanced := sandbox.AdvancedConfig{LDD: false, AddExec: true} diff --git a/internal/backend/tmux/tmux.go b/internal/backend/tmux/tmux.go index 0b9c9be..13a1593 100644 --- a/internal/backend/tmux/tmux.go +++ b/internal/backend/tmux/tmux.go @@ -125,7 +125,7 @@ func (b *Backend) CreateSession(ctx context.Context, opts backend.SessionOptions id := generateID() windowName := id // Use session ID as window name - debug.Log("[session %s] creating window in tmux session %s (role=%s, tasks=%v)", id, b.tmuxSession, opts.Role, opts.Tasks) + debug.Log("[session %s] creating window in tmux session %s (sandbox=%s)", id, b.tmuxSession, opts.Sandbox) // Build layered sandbox configuration // Start with global.yaml as base @@ -148,16 +148,16 @@ func (b *Backend) CreateSession(ctx context.Context, opts backend.SessionOptions } layers = append(layers, backendLayer) - // Add role layer if specified, otherwise use "default" role - role := opts.Role - if role == "" { - role = sandbox.DefaultRole + // Add sandbox layer if specified, otherwise use "default" + sbx := opts.Sandbox + if sbx == "" { + sbx = sandbox.DefaultSandbox } - roleLayer, err := sandbox.LoadRole(role) + sbxLayer, err := sandbox.LoadSandbox(sbx) if err != nil { - return nil, fmt.Errorf("failed to load role %q: %w", role, err) + return nil, fmt.Errorf("failed to load sandbox %q: %w", sbx, err) } - layers = append(layers, roleLayer) + layers = append(layers, sbxLayer) // Merge layers into final config general := sandbox.GeneralConfig{ @@ -348,8 +348,7 @@ func (b *Backend) CreateSession(ctx context.Context, opts backend.SessionOptions tmuxSession: b.tmuxSession, windowName: windowName, cwd: opts.CWD, - role: role, - tasks: opts.Tasks, + sandbox: sbx, model: opts.Model, modelResolver: b.cfg.ModelResolver, pid: pid, diff --git a/internal/p9/server.go b/internal/p9/server.go index 7a17292..ad63782 100644 --- a/internal/p9/server.go +++ b/internal/p9/server.go @@ -105,15 +105,14 @@ const ( fileAlias fileBackend fileContext - fileRole - fileTasks + fileSandbox fileTmux fileMail fileModel fileCount ) -var fileNames = []string{"ctl", "state", "pid", "cwd", "alias", "backend", "context", "role", "tasks", "tmux", "mail", "model"} +var fileNames = []string{"ctl", "state", "pid", "cwd", "alias", "backend", "context", "sandbox", "tmux", "mail", "model"} // Directory names in session var dirNames = []string{"inbox", "outbox", "completed"} @@ -700,7 +699,7 @@ func (s *Server) write(cs *connState, fc *plan9.Fcall) *plan9.Fcall { if path == "/ctl" { args := strings.Fields(input) if len(args) < 2 || args[0] != "new" { - return errFcall(fc, "usage: new [role=] [tasks=]") + return errFcall(fc, "usage: new [sandbox=] [model=]") } backendName := args[1] @@ -708,20 +707,15 @@ func (s *Server) write(cs *connState, fc *plan9.Fcall) *plan9.Fcall { if err != nil { return errFcall(fc, fmt.Sprintf("failed to get working directory: %v", err)) } - var role string - var tasks []string + var sbx string var model string // Parse remaining arguments: first non-key=value is cwd, rest are options cwdSet := false for i := 2; i < len(args); i++ { arg := args[i] - if r, ok := strings.CutPrefix(arg, "role="); ok { - role = r - } else if t, ok := strings.CutPrefix(arg, "tasks="); ok { - if t != "" { - tasks = strings.Split(t, ",") - } + if s, ok := strings.CutPrefix(arg, "sandbox="); ok { + sbx = s } else if m, ok := strings.CutPrefix(arg, "model="); ok { model = m } else if !cwdSet { @@ -753,10 +747,9 @@ func (s *Server) write(cs *connState, fc *plan9.Fcall) *plan9.Fcall { } opts := backend.SessionOptions{ - CWD: cleanPath, - Role: role, - Tasks: tasks, - Model: model, + CWD: cleanPath, + Sandbox: sbx, + Model: model, } _, err = s.mgr.New(opts, backendName) if err != nil { @@ -1550,10 +1543,8 @@ func (s *Server) getSessionFile(sess backend.Session, idx int) string { return tmuxSess.GetContext() } return "" - case fileRole: - return sess.Role() - case fileTasks: - return strings.Join(sess.Tasks(), ",") + case fileSandbox: + return sess.Sandbox() case fileTmux: tmuxSession, hasSession := meta.Extra["tmux_session"] tmuxWindow, hasWindow := meta.Extra["tmux_window"] diff --git a/internal/sandbox/config.go b/internal/sandbox/config.go index addbb23..5761e98 100644 --- a/internal/sandbox/config.go +++ b/internal/sandbox/config.go @@ -176,8 +176,8 @@ func Save(cfg *Config) error { return os.WriteFile(path, data, 0644) } -// DefaultRole is the role used when none is specified -const DefaultRole = "default" +// DefaultSandbox is the sandbox config used when none is specified +const DefaultSandbox = "default" // validateName checks if a role/task name is safe for use in file paths func validateName(name string) error { @@ -344,18 +344,18 @@ func LoadBackend(name string) (LayeredConfig, error) { return cfg, nil } -// LoadRole loads role-specific config -func LoadRole(name string) (LayeredConfig, error) { +// LoadSandbox loads sandbox-specific config +func LoadSandbox(name string) (LayeredConfig, error) { if err := validateName(name); err != nil { - return LayeredConfig{}, fmt.Errorf("invalid role name: %w", err) + return LayeredConfig{}, fmt.Errorf("invalid sandbox name: %w", err) } home, _ := os.UserHomeDir() - path := filepath.Join(home, ".config", "anvillm", "roles", name+".yaml") + path := filepath.Join(home, ".config", "anvillm", "sandbox", name+".yaml") data, err := os.ReadFile(path) if os.IsNotExist(err) { - return LayeredConfig{}, fmt.Errorf("role %q not found (looked in %s)", name, path) + return LayeredConfig{}, fmt.Errorf("sandbox %q not found (looked in %s)", name, path) } if err != nil { return LayeredConfig{}, err diff --git a/mcptools/create_session.sh b/mcptools/create_session.sh index c3a9afb..8fb85c9 100755 --- a/mcptools/create_session.sh +++ b/mcptools/create_session.sh @@ -1,7 +1,7 @@ #!/bin/bash # capabilities: agents # description: Create a new agent session -# Usage: create_session.sh [role=] [tasks=] [model=] +# Usage: create_session.sh [sandbox=] [model=] set -euo pipefail if cat /etc/shadow >/dev/null 2>&1; then @@ -10,7 +10,7 @@ if cat /etc/shadow >/dev/null 2>&1; then fi if [ $# -lt 2 ]; then - echo "usage: create_session.sh [role=] [tasks=] [model=]" >&2 + echo "usage: create_session.sh [sandbox=] [model=]" >&2 exit 1 fi diff --git a/skills/anvillm-sessions/SKILL.md b/skills/anvillm-sessions/SKILL.md index b7a1dcd..5e04bf8 100644 --- a/skills/anvillm-sessions/SKILL.md +++ b/skills/anvillm-sessions/SKILL.md @@ -13,7 +13,7 @@ Create and control agent sessions. All commands must be run via `execute_code` tool: - List sessions: `list_sessions.sh` -- Create session: `create_session.sh [role=] [tasks=] [model=]` +- Create session: `create_session.sh [sandbox=] [model=]` - Control session: `control_session.sh ` - Spawn agent: `spawn_agent.sh [cwd-path] [initial-context-prompt]` - Kill agent: `kill_agent.sh [cwd-path]` @@ -26,7 +26,7 @@ All commands must be run via `execute_code` tool: ## Session Lifecycle -1. Create: `create_session.sh kiro-cli /path/to/project role=developer` +1. Create: `create_session.sh kiro-cli /path/to/project sandbox=default` 2. Monitor: `list_sessions.sh` (check state field) 3. Control: `control_session.sh stop|restart|kill|refresh`