Send watched-pane-free signal only when something waits on the human

This commit is contained in:
Ian Gulliver
2026-10-22 21:34:12 -07:00
parent 0233e2c310
commit 73519bf4dd
3 changed files with 44 additions and 2 deletions
+34 -1
View File
@@ -497,7 +497,7 @@ func (m *Manager) leadTools(l *live) []mcp.Tool {
"If the pane already shows that task (or project), nothing changes and you are told so. "+
"Otherwise refused for 10 seconds after the human last moved the pane themselves (switching task), whatever else holds; if a show was refused for it, byre signals you \"The move hold is over\" when those 10 seconds pass without another move, and sends no signal otherwise. "+
"Otherwise refused while the human is working on what the pane shows (focused in it or in a view of its task, or with unsent text there), unless it shows a project with no task, or what it shows is finished (a merged or discarded task), "+
"or it shows a task whose agent is mid-turn, the human has no unsent text there, and either the human sent the message that started that turn since the pane last moved or the pane moved there 10 seconds ago or more; byre signals you \"Your right pane is free\" when either happens. "+
"or it shows a task whose agent is mid-turn, the human has no unsent text there, and either the human sent the message that started that turn since the pane last moved or the pane moved there 10 seconds ago or more; byre signals you \"Your right pane is free\" when the first happens, and when the second happens only while another task has something waiting on the human: an open question, a final message they haven't read, an error or rate limit nobody has messaged the agent since, or a suggestion to merge or discard. "+
"Also refused for 10 seconds after the human started a TODO item, unless they switched task or messaged you since; if a show was refused for it, byre signals you \"The start hold is over\" when those 10 seconds pass, and sends no signal otherwise.",
`{"type": "object", "properties": {
"task_id": {"type": "string"},
@@ -642,10 +642,43 @@ func (m *Manager) endWatchHold(ctx context.Context, login string, at time.Time)
if err != nil || !waiting {
return err
}
needed, err := m.needsHumanElsewhere(ctx, login, *f.TaskId)
if err != nil || !needed {
return err
}
text := fmt.Sprintf("Your right pane is free: the human has watched %s's agent work for 10 seconds without typing.", m.taskLabel(ctx, *f.TaskId))
return m.addLeadInput(ctx, store.LeadInput{Owner: login, Kind: store.LeadSignal, Actor: "byre", Text: text})
}
func (m *Manager) needsHumanElsewhere(ctx context.Context, login, shown string) (bool, error) {
tasks, err := m.store.ActiveTasks(ctx)
if err != nil {
return false, err
}
reads, err := m.store.SummaryReads(ctx, login)
if err != nil {
return false, err
}
for _, t := range tasks {
if t.Id == shown || t.Background {
continue
}
switch t.State {
case api.TaskStateAsking, api.TaskStateReadyToMerge, api.TaskStateDiscardSuggested:
return true, nil
case api.TaskStateIdle, api.TaskStateNoAgent:
if t.WorkflowStage == nil && t.SummarySeq > reads[t.Id] {
return true, nil
}
errored, err := m.store.ErrorUnanswered(ctx, t.Id)
if err != nil || errored {
return errored, err
}
}
}
return false, nil
}
func sameView(a, b api.Focus) bool {
at, bt := deref(a.TaskId), deref(b.TaskId)
if at != "" || bt != "" {