You've already forked agentic-coding-workflow
feat(acp): Supervisor.Spawn extraEnv parameter
- extraEnv overrides agent_kinds.encrypted_env same-name keys (spec §6.1 #11) - Prevents admin from hard-coding ACW_MCP_TOKEN in agent_kinds - SessionService passes nil for now; system token wiring lands in I4 - BuildSpawnEnv exported for testing the override priority - Fix app.go NewSupervisor call to match updated signature (from I5) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -27,6 +27,7 @@ import (
|
||||
"github.com/yan1h/agent-coding-workflow/internal/audit"
|
||||
"github.com/yan1h/agent-coding-workflow/internal/infra/crypto"
|
||||
"github.com/yan1h/agent-coding-workflow/internal/infra/notify"
|
||||
"github.com/yan1h/agent-coding-workflow/internal/mcp"
|
||||
"github.com/yan1h/agent-coding-workflow/internal/workspace"
|
||||
)
|
||||
|
||||
@@ -49,9 +50,10 @@ type Supervisor struct {
|
||||
repo Repository
|
||||
audit audit.Recorder
|
||||
notify *notify.Dispatcher
|
||||
wtSvc workspace.WorktreeService
|
||||
wsRepo workspace.Repository
|
||||
crypto *crypto.Encryptor
|
||||
wtSvc workspace.WorktreeService
|
||||
wsRepo workspace.Repository
|
||||
mcpTokens mcp.TokenService // spec §6.3 — onExit revokes system token
|
||||
crypto *crypto.Encryptor
|
||||
cfg SupervisorConfig
|
||||
log *slog.Logger
|
||||
}
|
||||
@@ -60,6 +62,7 @@ type Supervisor struct {
|
||||
// 起 goroutine,无主循环)。Stop 在 ShutdownAll 中实现。
|
||||
func NewSupervisor(repo Repository, rec audit.Recorder, disp *notify.Dispatcher,
|
||||
wtSvc workspace.WorktreeService, wsRepo workspace.Repository,
|
||||
mcpTokens mcp.TokenService,
|
||||
enc *crypto.Encryptor, cfg SupervisorConfig, log *slog.Logger) *Supervisor {
|
||||
if log == nil {
|
||||
log = slog.Default()
|
||||
@@ -71,6 +74,7 @@ func NewSupervisor(repo Repository, rec audit.Recorder, disp *notify.Dispatcher,
|
||||
notify: disp,
|
||||
wtSvc: wtSvc,
|
||||
wsRepo: wsRepo,
|
||||
mcpTokens: mcpTokens,
|
||||
crypto: enc,
|
||||
cfg: cfg,
|
||||
log: log,
|
||||
@@ -143,12 +147,17 @@ func (s *Supervisor) GetRelay(sid uuid.UUID) *Relay {
|
||||
// Spawn 启动一个子进程并完成 ACP handshake。返回的 *Process 已注册到 procs map;
|
||||
// handshake 失败时调用 Kill 回滚并返回错误。调用方(SessionService)负责前置的
|
||||
// worktree 占用与 acp_sessions 行 INSERT;本方法仅负责 OS 层资源与协议层握手。
|
||||
func (s *Supervisor) Spawn(ctx context.Context, sess *Session, kind *AgentKind) (*Process, error) {
|
||||
func (s *Supervisor) Spawn(ctx context.Context, sess *Session, kind *AgentKind, extraEnv map[string]string) (*Process, error) {
|
||||
envMap, err := decryptEnv(s.crypto, kind.EncryptedEnv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// System override: extraEnv overrides agent_kinds same-name keys (spec §6.1 #11)
|
||||
for k, v := range extraEnv {
|
||||
envMap[k] = v
|
||||
}
|
||||
|
||||
// strip env:只保留 PATH + 解密 overrides,避免泄漏 master key、git 凭据等。
|
||||
osEnv := []string{"PATH=" + os.Getenv("PATH")}
|
||||
for k, v := range envMap {
|
||||
@@ -258,6 +267,22 @@ func (s *Supervisor) handshake(ctx context.Context, sess *Session, relay *Relay,
|
||||
return nil
|
||||
}
|
||||
|
||||
// BuildSpawnEnv exposes env merge logic for testing.
|
||||
func (s *Supervisor) BuildSpawnEnv(kind *AgentKind, extraEnv map[string]string) ([]string, error) {
|
||||
envMap, err := decryptEnv(s.crypto, kind.EncryptedEnv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for k, v := range extraEnv {
|
||||
envMap[k] = v
|
||||
}
|
||||
out := []string{"PATH=" + os.Getenv("PATH")}
|
||||
for k, v := range envMap {
|
||||
out = append(out, fmt.Sprintf("%s=%s", k, v))
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// Kill 终止指定 session 的子进程。在 Unix 平台上先发 SIGTERM 等待 grace 时间,
|
||||
// 仍未退出再 Process.Kill;Windows 没有可靠 SIGTERM 实现,直接 Process.Kill。
|
||||
// 始终阻塞直到 monitorProcess 关闭 done channel。Kill 是幂等的——目标 session
|
||||
@@ -416,6 +441,13 @@ func (s *Supervisor) onExit(ctx context.Context, proc *Process, status SessionSt
|
||||
}
|
||||
}
|
||||
_ = waitErr // 已通过 KilledByUs / ExitError 编码到 status / exitCode
|
||||
// Revoke session's system MCP tokens (spec §6.3)
|
||||
if s.mcpTokens != nil {
|
||||
if err := s.mcpTokens.RevokeBySession(ctx, proc.SessionID); err != nil {
|
||||
s.log.Error("acp.on_exit.mcp_revoke",
|
||||
"session_id", proc.SessionID, "err", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// 最后关 relay:广播 session_terminated 给 WS subscribers。
|
||||
proc.Relay.Close(string(status), &exitCode)
|
||||
|
||||
Reference in New Issue
Block a user