Lock the right pane while focused, add project focus, quiet lead

This commit is contained in:
Ian Gulliver
2026-10-15 09:59:13 -07:00
parent bdd1ae48f0
commit fdd005b591
32 changed files with 576 additions and 227 deletions
+12 -23
View File
@@ -15,8 +15,14 @@ import (
)
const leadGuidance = `You are the human's lead agent in byre, shown in the left pane of byre's web UI. The human talks to you there, and you coordinate every task agent on their behalf. You don't do task work yourself: task agents work in their own worktrees, and you read, direct and report on them.
- Signals: byre sends you, as messages from byre, what happens across tasks: questions, turn reports and errors that task agents file, questions the human answered, tasks merged or discarded, and the human switching the right pane. Several may arrive in one message. Messages from the human arrive separately.
- Direct the human's attention. The right pane always shows one task; point it with show (the task, the tabs to put first, the tab to select). When something needs the human, show it and say in a sentence or two what it is and what you recommend. Don't move the pane for things that don't need them, and don't pull it away from what they are looking at unless the new thing is more urgent.
- Signals: byre sends you, as messages from byre, what happens across tasks: questions, turn reports and errors that task agents file, questions the human answered, tasks merged or discarded. Several may arrive in one message. Messages from the human arrive separately.
- Don't respond to what the human just did. A signal about their own action (a question they answered, a task they merged or discarded) tells them nothing new: don't report, confirm or comment on it. Act on it only if it changes what you are doing, and then end the turn with one short line at most. When a batch of signals needs nothing from you to the human, make your final message exactly NO_REPLY and nothing else; byre folds that turn away so the human doesn't see it.
- Steering the right pane is a core part of your job, as important as what you write. The right pane shows one task, or a project with no task; point it with show (the task or project, the tabs to put first, the tab to select). By default, keep it on whatever you and the human are discussing, so they can see it while they read your reply; don't make them go looking for it. Call show in the same turn you bring something up:
- Discussing, picking or starting TODO items: the project's todo tab (show with project_id when no task of that project is involved).
- A question or report that needs the human: that task's inbox tab, and say in a sentence or two what it is and what you recommend.
- Discussing a task's work, what its agent did or is doing, or whether it is ready to merge: its transcript tab. A specific file in its worktree: its files tab.
- The changes tab only when the human asks to see the diff; they rarely want it.
- Don't move the pane for things that don't need the human, and don't pull it away from what they are looking at unless the new thing is more urgent. While the human is working in the right pane, show is refused: say in chat what you would have shown, and carry on.
- Questions from task agents: answer one yourself with answer_question only when the answer is clear, for example the agent's recommendation is plainly right given what the human has said, or the human already decided it. Otherwise show it to the human with your recommendation and wait. When the human tells you an answer, give it with answer_question. Say which questions you answered and how.
- Reports: read them and tell the human only what matters: work finished and ready to merge, problems, decisions made. Stay quiet about routine progress.
- Dig into context with list_tasks, read_transcript and read_inbox, and with your own Read, Grep and Bash on a task's worktree path (from list_tasks). Don't edit files in task worktrees; ask the task's agent with send_message.
@@ -245,7 +251,7 @@ func (m *Manager) leadTurnEnded(ctx context.Context, l *live, res claude.Result)
func (m *Manager) Observe(ev api.Event) {
switch ev.Kind {
case "inbox.created", "inbox.updated", "task.merged", "task.discarded", "focus.updated":
case "inbox.created", "inbox.updated", "task.merged", "task.discarded":
default:
return
}
@@ -271,15 +277,9 @@ func (m *Manager) signalLoop() {
}
func (m *Manager) signal(ctx context.Context, ev api.Event) error {
owners := []string{}
if ev.Owner != nil {
owners = append(owners, *ev.Owner)
} else {
all, err := m.store.LeadOwners(ctx)
if err != nil {
return err
}
owners = all
owners, err := m.store.LeadOwners(ctx)
if err != nil {
return err
}
text, err := m.signalText(ctx, ev)
@@ -334,17 +334,6 @@ func (m *Manager) signalText(ctx context.Context, ev api.Event) (string, error)
case "task.discarded":
return fmt.Sprintf("%s was discarded by %s.", m.taskLabel(ctx, deref(ev.TaskId)), ev.Actor), nil
case "focus.updated":
f := api.Focus{}
err := remarshal(ev.Payload, &f)
if err != nil {
return "", err
}
if f.TaskId == nil {
return "The human cleared the right pane.", nil
}
return fmt.Sprintf("The human switched the right pane to %s, tab %s.", m.taskLabel(ctx, *f.TaskId), f.Tab), nil
}
return "", nil
}
+55 -1
View File
@@ -1,6 +1,7 @@
package agents_test
import (
"fmt"
"strings"
"testing"
"time"
@@ -66,8 +67,61 @@ func TestLeadSignalsAndShow(t *testing.T) {
if err != nil {
t.Fatalf("get focus: %v", err)
}
if f.TaskId == nil || *f.TaskId != e.task.Id || f.Tab != api.TabInbox {
if f.TaskId == nil || *f.TaskId != e.task.Id || f.ProjectId == nil || *f.ProjectId != e.project.Id || f.Tab != api.TabInbox {
t.Fatalf("focus %+v", f)
}
e.waitFor("lead idle after show", leadIdle)
m.SetPaneFocus(login, true)
_, err = m.MessageLead(e.ctx, e.user, "Call the show tool with task_id "+e.task.Id+", tabs [\"changes\"] and tab \"changes\". Then say done.", "", nil)
if err != nil {
t.Fatalf("message lead: %v", err)
}
e.waitFor("show refused", func(ev api.Event) bool {
return ev.Kind == "agent.tool_result" && ev.Owner != nil && strings.Contains(fmt.Sprint(ev.Payload), "working in the right pane")
})
e.waitFor("lead idle after refused show", leadIdle)
f, err = e.store.GetFocus(e.ctx, login)
if err != nil {
t.Fatalf("get focus: %v", err)
}
if f.Tab != api.TabInbox {
t.Fatalf("focus moved while the pane was focused: %+v", f)
}
}
func TestFocusProject(t *testing.T) {
t.Parallel()
e := newEnv(t)
m := e.manager()
err := m.SetFocus(e.ctx, login, api.Focus{ProjectId: &e.project.Id, Tabs: []api.TaskTab{api.TabTodo}, Tab: api.TabTodo}, login)
if err != nil {
t.Fatalf("focus project: %v", err)
}
f, err := e.store.GetFocus(e.ctx, login)
if err != nil {
t.Fatalf("get focus: %v", err)
}
if f.TaskId != nil || f.ProjectId == nil || *f.ProjectId != e.project.Id || f.Tab != api.TabTodo {
t.Fatalf("focus %+v", f)
}
other := "nope"
err = m.SetFocus(e.ctx, login, api.Focus{TaskId: &e.task.Id, ProjectId: &other, Tabs: []api.TaskTab{}, Tab: api.TabTranscript}, login)
if err != nil {
t.Fatalf("focus task: %v", err)
}
f, err = e.store.GetFocus(e.ctx, login)
if err != nil {
t.Fatalf("get focus: %v", err)
}
if f.TaskId == nil || *f.TaskId != e.task.Id || f.ProjectId == nil || *f.ProjectId != e.project.Id {
t.Fatalf("focus %+v", f)
}
err = m.SetFocus(e.ctx, login, api.Focus{ProjectId: &other, Tabs: []api.TaskTab{}, Tab: api.TabTodo}, login)
if err == nil {
t.Fatal("focus on an unknown project was accepted")
}
}
+26 -3
View File
@@ -292,10 +292,12 @@ func (m *Manager) leadTools(l *live) []mcp.Tool {
}),
tool("show",
"Point the human's right pane at a task: tabs are the tabs to put first, in order (the rest follow), and tab is the one to select. "+
"Tabs: transcript, changes, files, inbox (the task's open items), todo (the project's TODO.md). Omit task_id to clear the pane.",
"Point the human's right pane at a task, or at a project with no task: tabs are the tabs to put first, in order (the rest follow), and tab is the one to select. "+
"Tabs: transcript, changes, files, inbox (the task's open items), todo (the project's TODO.md); with only project_id, only todo shows anything. "+
"Omit both task_id and project_id to clear the pane. Refused while the human is working in the right pane.",
`{"type": "object", "properties": {
"task_id": {"type": "string"},
"project_id": {"type": "string", "description": "Ignored when task_id is given."},
"tabs": {"type": "array", "items": {"type": "string", "enum": ["transcript", "changes", "files", "inbox", "todo"]}},
"tab": {"type": "string", "enum": ["transcript", "changes", "files", "inbox", "todo"]}
}, "required": ["tabs", "tab"]}`,
@@ -305,6 +307,12 @@ func (m *Manager) leadTools(l *live) []mcp.Tool {
if err != nil {
return "", err
}
m.mu.Lock()
focused := m.paneFocused[user.Login]
m.mu.Unlock()
if focused {
return "", fmt.Errorf("%w: the human is working in the right pane, so it was left as it is; tell them in chat instead", ErrConflict)
}
err = m.SetFocus(ctx, user.Login, f, leadActor(user.Login))
if err != nil {
return "", err
@@ -333,15 +341,30 @@ func (m *Manager) SetFocus(ctx context.Context, login string, f api.Focus, actor
if f.TaskId != nil && *f.TaskId == "" {
f.TaskId = nil
}
if f.ProjectId != nil && *f.ProjectId == "" {
f.ProjectId = nil
}
if f.TaskId != nil {
_, _, err := m.activeTask(ctx, *f.TaskId)
task, _, err := m.activeTask(ctx, *f.TaskId)
if err != nil {
return err
}
f.ProjectId = &task.ProjectId
} else if f.ProjectId != nil {
_, err := m.store.GetProject(ctx, *f.ProjectId)
if err != nil {
return fmt.Errorf("project %s: %w", *f.ProjectId, err)
}
}
return m.store.SetFocus(ctx, login, f, actor)
}
func (m *Manager) SetPaneFocus(login string, focused bool) {
m.mu.Lock()
defer m.mu.Unlock()
m.paneFocused[login] = focused
}
func (m *Manager) modelOr(model string, l *live) string {
if model != "" {
return model
+2
View File
@@ -53,6 +53,7 @@ type Manager struct {
rateLimits map[api.AgentSessionVendor]map[string]any
merges map[string]string
leadAnswered map[string]bool
paneFocused map[string]bool
deliverMu sync.Mutex
capMu sync.Mutex
@@ -100,6 +101,7 @@ func New(st *store.Store, limits Limits, worktreeRoot, leadRoot string) *Manager
rateLimits: map[api.AgentSessionVendor]map[string]any{},
merges: map[string]string{},
leadAnswered: map[string]bool{},
paneFocused: map[string]bool{},
signals: make(chan api.Event, 1024),
snaps: map[string]*snapState{},
bad: newBadLines(),
+166 -135
View File
@@ -539,7 +539,9 @@ type FileDiff struct {
// Focus defines model for Focus.
type Focus struct {
Tab TaskTab `json:"tab"`
// ProjectId The project the right pane shows; the task's project when task_id is set
ProjectId *string `json:"project_id,omitempty"`
Tab TaskTab `json:"tab"`
// Tabs The tabs the lead put first, in order. The other task tabs follow them.
Tabs []TaskTab `json:"tabs"`
@@ -641,6 +643,12 @@ type NotificationPrefs struct {
Reports bool `json:"reports"`
}
// PaneFocus defines model for PaneFocus.
type PaneFocus struct {
// Focused True while the browser window has focus and the focused element is inside the right pane.
Focused bool `json:"focused"`
}
// Participant defines model for Participant.
type Participant struct {
DisplayName string `json:"display_name"`
@@ -971,6 +979,9 @@ type MessageLeadJSONRequestBody = MessageCreate
// UpdateNotificationPrefsJSONRequestBody defines body for UpdateNotificationPrefs for application/json ContentType.
type UpdateNotificationPrefsJSONRequestBody = NotificationPrefs
// SetPaneFocusJSONRequestBody defines body for SetPaneFocus for application/json ContentType.
type SetPaneFocusJSONRequestBody = PaneFocus
// AddPushSubscriptionJSONRequestBody defines body for AddPushSubscription for application/json ContentType.
type AddPushSubscriptionJSONRequestBody = PushSubscription
@@ -1034,6 +1045,9 @@ type ServerInterface interface {
// (PUT /api/me/notifications)
UpdateNotificationPrefs(w http.ResponseWriter, r *http.Request)
// (PUT /api/me/pane-focus)
SetPaneFocus(w http.ResponseWriter, r *http.Request)
// (DELETE /api/me/push-subscriptions)
RemovePushSubscription(w http.ResponseWriter, r *http.Request, params RemovePushSubscriptionParams)
@@ -1462,6 +1476,20 @@ func (siw *ServerInterfaceWrapper) UpdateNotificationPrefs(w http.ResponseWriter
handler.ServeHTTP(w, r)
}
// SetPaneFocus operation middleware
func (siw *ServerInterfaceWrapper) SetPaneFocus(w http.ResponseWriter, r *http.Request) {
handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
siw.Handler.SetPaneFocus(w, r)
}))
for _, middleware := range siw.HandlerMiddlewares {
handler = middleware(handler)
}
handler.ServeHTTP(w, r)
}
// RemovePushSubscription operation middleware
func (siw *ServerInterfaceWrapper) RemovePushSubscription(w http.ResponseWriter, r *http.Request) {
@@ -2478,6 +2506,7 @@ func HandlerWithOptions(si ServerInterface, options StdHTTPServerOptions) http.H
m.HandleFunc(http.MethodPost+" "+options.BaseURL+"/api/me/lead/messages", wrapper.MessageLead)
m.HandleFunc(http.MethodGet+" "+options.BaseURL+"/api/me/focus", wrapper.GetFocus)
m.HandleFunc(http.MethodPut+" "+options.BaseURL+"/api/me/focus", wrapper.SetFocus)
m.HandleFunc(http.MethodPut+" "+options.BaseURL+"/api/me/pane-focus", wrapper.SetPaneFocus)
m.HandleFunc(http.MethodGet+" "+options.BaseURL+"/api/push/key", wrapper.GetPushKey)
m.HandleFunc(http.MethodGet+" "+options.BaseURL+"/api/projects", wrapper.ListProjects)
m.HandleFunc(http.MethodPost+" "+options.BaseURL+"/api/projects", wrapper.CreateProject)
@@ -2521,140 +2550,142 @@ func HandlerWithOptions(si ServerInterface, options StdHTTPServerOptions) http.H
// const string: with thousands of chunks the chained `+` fold is several
// times slower for the Go compiler than parsing a slice literal.
var swaggerSpec = []string{
"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",
}
// decodeSpec returns the embedded OpenAPI spec as raw JSON bytes,
+9
View File
@@ -80,3 +80,12 @@ func (s *Server) SetFocus(w http.ResponseWriter, r *http.Request) {
}
writeJSON(w, http.StatusOK, f)
}
func (s *Server) SetPaneFocus(w http.ResponseWriter, r *http.Request) {
p := api.PaneFocus{}
if !readJSON(w, r, &p) {
return
}
s.agents.SetPaneFocus(actor(r), p.Focused)
w.WriteHeader(http.StatusNoContent)
}
+4
View File
@@ -244,6 +244,10 @@ CREATE TABLE lead_inputs (
created_at TEXT NOT NULL
);
CREATE INDEX lead_inputs_owner ON lead_inputs(owner, id);
`}, {"add-focus-project", `
UPDATE users SET focus = json_set(focus, '$.project_id', (SELECT project_id FROM tasks WHERE id = json_extract(users.focus, '$.task_id')))
WHERE json_extract(focus, '$.task_id') IS NOT NULL;
DELETE FROM lead_inputs WHERE text LIKE 'The human switched the right pane %' OR text = 'The human cleared the right pane.';
`}}
type Store struct {