This commit is contained in:
2026-06-09 21:19:30 +08:00
parent 8b41ff9360
commit 0484e79978
66 changed files with 4043 additions and 533 deletions
+33 -16
View File
@@ -47,17 +47,22 @@ type Supervisor struct {
mu sync.RWMutex
procs map[uuid.UUID]*Process
repo Repository
audit audit.Recorder
notify *notify.Dispatcher
repo Repository
audit audit.Recorder
notify *notify.Dispatcher
wtSvc workspace.WorktreeService
wsRepo workspace.Repository
mcpTokens mcp.TokenService // spec §6.3 — onExit revokes system token
crypto *crypto.Encryptor
cfg SupervisorConfig
log *slog.Logger
permSvc *PermissionService // 工具调用权限审批;可为 nil(部分测试)
cfg SupervisorConfig
log *slog.Logger
}
// SetPermissionService 注入权限审批服务。与 Supervisor 互相依赖,故装配时回填
// (permSvc 需要 Supervisor 作为 RelayLocator,Supervisor 需要 permSvc 注入 handler)。
func (s *Supervisor) SetPermissionService(p *PermissionService) { s.permSvc = p }
// NewSupervisor 构造空 Supervisor。Start 不需要 — supervisor 是被动的(spawn 时
// 起 goroutine,无主循环)。Stop 在 ShutdownAll 中实现。
func NewSupervisor(repo Repository, rec audit.Recorder, disp *notify.Dispatcher,
@@ -68,16 +73,16 @@ func NewSupervisor(repo Repository, rec audit.Recorder, disp *notify.Dispatcher,
log = slog.Default()
}
return &Supervisor{
procs: map[uuid.UUID]*Process{},
repo: repo,
audit: rec,
notify: disp,
wtSvc: wtSvc,
wsRepo: wsRepo,
procs: map[uuid.UUID]*Process{},
repo: repo,
audit: rec,
notify: disp,
wtSvc: wtSvc,
wsRepo: wsRepo,
mcpTokens: mcpTokens,
crypto: enc,
cfg: cfg,
log: log,
crypto: enc,
cfg: cfg,
log: log,
}
}
@@ -95,7 +100,7 @@ type Process struct {
Stderr io.ReadCloser
Relay *Relay
StderrBuf *stderrRing // 排障 ring buffer
StderrBuf *stderrRing // 排障 ring buffer
StartedAt time.Time
KilledByUs atomic.Bool // true → exited,false → crashed
done chan struct{} // monitor 退出时 close
@@ -185,13 +190,17 @@ func (s *Supervisor) Spawn(ctx context.Context, sess *Session, kind *AgentKind,
return nil, fmt.Errorf("start: %w", err)
}
var decider handlers.PermissionDecider
if s.permSvc != nil {
decider = s.permSvc
}
relay := NewRelay(
sess.ID,
NewDecoder(stdout, s.cfg.EventMaxPayload),
NewEncoder(stdin),
s.repo,
handlers.NewFsHandler(),
handlers.NewPermissionHandler(),
handlers.NewPermissionHandler(decider),
RelayConfig{
EventMaxPayload: s.cfg.EventMaxPayload,
EventTruncateField: s.cfg.EventTruncateField,
@@ -227,6 +236,7 @@ func (s *Supervisor) Spawn(ctx context.Context, sess *Session, kind *AgentKind,
UserID: sess.UserID,
CwdPath: sess.CwdPath,
AgentSessionID: sess.AgentSessionID,
ToolAllowlist: kind.ToolAllowlist,
})
if err := s.handshake(ctx, sess, relay, proc); err != nil {
@@ -441,6 +451,13 @@ func (s *Supervisor) onExit(ctx context.Context, proc *Process, status SessionSt
}
}
_ = waitErr // 已通过 KilledByUs / ExitError 编码到 status / exitCode
// 终止会话的全部挂起权限请求:标 expired 并唤醒阻塞的 Decide goroutine,
// 避免 agent 端永久挂起。须在 Relay.Close 之前(Close 后 FanoutControl 静默丢弃)。
if s.permSvc != nil {
s.permSvc.CancelSession(ctx, proc.SessionID)
}
// Revoke session's system MCP tokens (spec §6.3)
if s.mcpTokens != nil {
if err := s.mcpTokens.RevokeBySession(ctx, proc.SessionID); err != nil {