Lead show moved pane despite unsent text

This commit is contained in:
Ian Gulliver
2026-10-18 09:20:01 -07:00
parent 1363a3604d
commit 5716077869
5 changed files with 61 additions and 8 deletions
+37 -3
View File
@@ -1,6 +1,12 @@
package agents
import "g.fc.run/misc/byre/internal/mcp"
import (
"os"
"os/exec"
"path/filepath"
"g.fc.run/misc/byre/internal/mcp"
)
const screenshotGuidance = "\n- When a turn changes something you can see, you can show it in your final message: save screenshots with the Playwright tools (pages you serve on this task's ports) or hands' capture with save (apps on this Mac), and embed each as a Markdown image, ![what it shows](<path>), with the path the tool returned. They go to the task's media folder, outside the worktree, so they are never committed, and are deleted when the task closes. Leave out a screenshot that adds nothing to the text."
@@ -8,6 +14,34 @@ func MediaDir(worktreePath string) string {
return worktreePath + "-media"
}
func handsMCP(mediaDir string) mcp.Stdio {
return mcp.Stdio{Name: "hands", Command: "byre-hands", Args: []string{"--media-dir", mediaDir}}
func (m *Manager) handsDir(sessionID string) string {
return filepath.Join(m.handsRoot, sessionID)
}
// replayd keys ScreenCaptureKit clients by executable path, so two hands
// processes run from one path keep cancelling each other's connection.
func (m *Manager) handsMCP(sessionID, mediaDir string) (mcp.Stdio, error) {
src, err := exec.LookPath("byre-hands")
if err != nil {
return mcp.Stdio{}, err
}
data, err := os.ReadFile(src)
if err != nil {
return mcp.Stdio{}, err
}
dir := m.handsDir(sessionID)
err = os.RemoveAll(dir)
if err != nil {
return mcp.Stdio{}, err
}
err = os.MkdirAll(dir, 0o755)
if err != nil {
return mcp.Stdio{}, err
}
bin := filepath.Join(dir, "byre-hands")
err = os.WriteFile(bin, data, 0o755)
if err != nil {
return mcp.Stdio{}, err
}
return mcp.Stdio{Name: "hands", Command: bin, Args: []string{"--media-dir", mediaDir}}, nil
}
+13 -2
View File
@@ -37,6 +37,7 @@ type Manager struct {
store *store.Store
worktreeRoot string
leadRoot string
handsRoot string
codexHome string
status func(context.Context) (api.Status, error)
addProject func(ctx context.Context, name, repoPath, createdBy string) (api.Project, error)
@@ -122,11 +123,12 @@ func (l *live) implementer() bool {
return l.owner == "" && l.reviewID == ""
}
func New(st *store.Store, worktreeRoot, leadRoot, codexHome string, status func(context.Context) (api.Status, error), addProject func(ctx context.Context, name, repoPath, createdBy string) (api.Project, error), clientRegistry *clients.Registry) *Manager {
func New(st *store.Store, worktreeRoot, leadRoot, handsRoot, codexHome string, status func(context.Context) (api.Status, error), addProject func(ctx context.Context, name, repoPath, createdBy string) (api.Project, error), clientRegistry *clients.Registry) *Manager {
m := &Manager{
store: st,
worktreeRoot: worktreeRoot,
leadRoot: leadRoot,
handsRoot: handsRoot,
codexHome: codexHome,
status: status,
addProject: addProject,
@@ -768,7 +770,11 @@ func (m *Manager) startLive(ctx context.Context, sess api.AgentSession, task api
return nil, err
}
g += screenshotGuidance
stdio = []mcp.Stdio{handsMCP(media), playwrightMCP(media)}
hands, err := m.handsMCP(sess.Id, media)
if err != nil {
return nil, err
}
stdio = []mcp.Stdio{hands, playwrightMCP(media)}
}
if sess.Role == api.Reviewer {
r, err := m.store.ReviewBySession(ctx, sess.Id)
@@ -819,6 +825,11 @@ func (m *Manager) pump(l *live) {
l.sess.drive(m, l)
err := os.RemoveAll(m.handsDir(l.id))
if err != nil {
slog.Error("remove session's byre-hands", "session", l.id, "err", err)
}
m.mu.Lock()
delete(m.live, l.id)
closing := m.closing
+1 -1
View File
@@ -68,7 +68,7 @@ func New(dataDir string, portFirst int) (*Server, error) {
}
}
s.agents = agents.New(s.store, filepath.Join(dataDir, "worktrees"), filepath.Join(dataDir, "leads"), filepath.Join(dataDir, "codex"), s.status, s.createProject, s.clients)
s.agents = agents.New(s.store, filepath.Join(dataDir, "worktrees"), filepath.Join(dataDir, "leads"), filepath.Join(dataDir, "hands"), filepath.Join(dataDir, "codex"), s.status, s.createProject, s.clients)
err = s.agents.Recover(context.Background())
if err != nil {
s.store.Close()