package daemon // isolation.go - 会话隔离策略接口及内置实现. // // 解决问题:不同场景下,Agent 会话需要不同程度的隔离: // - 共享模式(Shared):同进程,同目录,适合单用户多对话 // - 隔离模式(Isolated):独立 context 树 + 工作目录锁定,适合多用户 // - 工作树模式(Worktree):独立 Git worktree,适合并行代码修改任务 // // 升华改进(ELEVATED): 早期方案 的 SpawnMode 用 child_process.spawn // 创建子进程实现隔离--Go 有 goroutine,无需子进程. // 我们用 SessionIsolation 接口封装隔离逻辑,DaemonManager 不关心实现细节: // - CLI/IDE 场景:SharedIsolation(最轻量) // - 仓储 SaaS 多租户:IsolatedIsolation(独立 context + 路径限制) // - 代码并行修改:WorktreeIsolation(独立 Git worktree,P1 未实现) // // 替代方案: - 否决:新增隔离模式需改核心代码, // 接口方式让三方扩展零侵入. import ( "context" "fmt" "os" "path/filepath" ) // SessionIsolation 定义会话的环境隔离策略. // // Setup 在会话启动前调用,Teardown 在会话结束后调用(无论是否出错). // 每个会话独立的 SessionIsolation 实例,不跨会话共享. type SessionIsolation interface { // Setup 准备会话运行环境. // 返回实际的工作目录(可能与传入的 baseDir 不同,如 Worktree 模式). Setup(ctx context.Context, sessionID string, baseDir string) (workDir string, err error) // Teardown 清理会话运行环境. // ctx 已超时/取消时 Teardown 仍需执行(使用 context.Background() 或超时). Teardown(ctx context.Context, sessionID string) error // Name 返回隔离模式名称(用于日志和监控). Name() string } // ─── SharedIsolation:共享模式(最轻量)──────────────────────────────────────── // SharedIsolation 不做任何隔离,所有会话共享同一工作目录和进程环境. // 适用场景:单用户本地开发,CLI 模式,测试环境. // // 精妙之处(CLEVER): 即使是"无隔离"也实现 SessionIsolation 接口-- // DaemonManager 代码路径统一,不需要 if isolation == nil 的特殊处理. // 这是 Null Object Pattern 的应用. type SharedIsolation struct{} func (SharedIsolation) Setup(_ context.Context, _ string, baseDir string) (string, error) { return baseDir, nil } func (SharedIsolation) Teardown(_ context.Context, _ string) error { return nil } func (SharedIsolation) Name() string { return "shared" } // ─── IsolatedIsolation:隔离模式────────────────────────────────────────��─────── // IsolatedIsolation 为每个会话创建独立的工作目录(沙箱子目录). // 适用场景:多用户 SaaS,飞驼多操作员并发,不同项目的并行会话. // // 工作目录结构: // // baseDir/ // sessions/ // / ← 每个会话的独立目录 // workdir/ ← Agent 实际工作目录(写操作限制在此范围内) // // 升华改进(ELEVATED): 早期方案没有工作目录隔离--多用户场景下不同 Agent 会话 // 可能互相覆盖彼此的文件(同一目录并发写入). // 我们为每个隔离会话创建独立的 workdir,配合引擎的 Cwd 配置实现路径限制. // 替代方案: - 否决:需要 root 权限,跨平台兼容性差; // 应用层路径限制(Cwd + 引擎路径校验)安全性足够且零特权. type IsolatedIsolation struct { SessionsDir string // 存放所有会话目录的根目录,默认 /sessions } func (iso *IsolatedIsolation) Setup(_ context.Context, sessionID string, baseDir string) (string, error) { sessionsDir := iso.SessionsDir if sessionsDir == "" { sessionsDir = filepath.Join(baseDir, "sessions") } workDir := filepath.Join(sessionsDir, sessionID, "workdir") if err := os.MkdirAll(workDir, 0o755); err != nil { return "", fmt.Errorf("isolation: create workdir for %s: %w", sessionID, err) } return workDir, nil } func (iso *IsolatedIsolation) Teardown(_ context.Context, sessionID string) error { // 历史包袱(LEGACY): 当前 Teardown 不清理会话目录-- // 会话结束后保留目录便于调试(查看 Agent 操作了哪些文件). // 理想做法:根据 RetainSessionDir 配置决定是否清理. // 什么时候改:接入审计存储后,文件可以归档到数据库,本地目录就可以清理了. _ = sessionID return nil } func (iso *IsolatedIsolation) Name() string { return "isolated" } // ─── 工厂函数 ───────────────────────────────────────────────────────────────── // IsolationMode 是隔离模式的枚举类型. type IsolationMode string const ( // IsolationShared 共享模式,不隔离(单用户/开发环境). IsolationShared IsolationMode = "shared" // IsolationIsolated 隔离模式,独立工作目录(多用户/生产环境). IsolationIsolated IsolationMode = "isolated" ) // NewIsolation 根据模式创建对应的 SessionIsolation 实现. func NewIsolation(mode IsolationMode) SessionIsolation { switch mode { case IsolationIsolated: return &IsolatedIsolation{} default: return SharedIsolation{} } }