ask_human nudge to merge main
This commit is contained in:
+1
-1
@@ -319,7 +319,7 @@ The **Project** tab is the dashboard of the right pane's project (the focused ta
|
||||
|
||||
## Inbox
|
||||
|
||||
Agents ask questions with the `ask_human` MCP tool. Each question carries a `context` with the background the human needs to answer without reading the transcript, and its choices are written inline in that context as Markdown list items: `- ( ) text` for pick-one choices, with exactly one `- (*) text` marking the recommendation, or `- [ ] text` for pick-any choices, with `- [x] text` marking the recommended ones. There is no separate list of options. byre parses the choices out of `context` (`internal/agents/choices.go`; list items inside code fences and blockquotes don't count) and stores their text in the payload as `options`, with `multiple` (pick-any) and `recommended`. The UI renders the context as Markdown with a radio button or checkbox in place of each choice's marker. The tool's description tells agents to keep everything about one choice (what it does, its example, code, trade-offs) in lines indented beneath that choice rather than in text above the list; those lines render inside the option, and only the marker line's text is the option. When every item of a choice list has exactly one non-blank indented line and that line is only a Markdown image, the list renders as a grid of tiles (`grid`, set in `parseList` in `mac/Sources/Byre/Markdown.swift`): four across, or two across when the context is at most 480pt wide. The tool's description tells agents to write image choices this way. A malformed call (no `context` or `why_blocked`, fewer than two choices, pick-one and pick-any choices mixed, a pick-one question without exactly one recommendation, two choices with the same text, an unknown attachment kind, a foreign `thread_id`) is rejected with a message saying what to fix, and nothing is filed. All questions are alike: the tool's description and its result tell the agent to end its turn after asking, and the answer arrives as its next message. The call returns the item's id as `question_id`. An agent withdraws a question it no longer needs answered (the human answered it in a chat message, say) with the `cancel_question` MCP tool and that id: an `open` question of its own session becomes `dismissed` with no `resolved_by` (its `inbox.updated` event has actor `agent`), which also stops it from holding back delivery; anything else is rejected with a message saying why. The inbox holds only questions; errors go in the transcript (see "Errors").
|
||||
Agents ask questions with the `ask_human` MCP tool. Each question carries a `context` with the background the human needs to answer without reading the transcript, and its choices are written inline in that context as Markdown list items: `- ( ) text` for pick-one choices, with exactly one `- (*) text` marking the recommendation, or `- [ ] text` for pick-any choices, with `- [x] text` marking the recommended ones. There is no separate list of options. byre parses the choices out of `context` (`internal/agents/choices.go`; list items inside code fences and blockquotes don't count) and stores their text in the payload as `options`, with `multiple` (pick-any) and `recommended`. The UI renders the context as Markdown with a radio button or checkbox in place of each choice's marker. The tool's description tells agents to keep everything about one choice (what it does, its example, code, trade-offs) in lines indented beneath that choice rather than in text above the list; those lines render inside the option, and only the marker line's text is the option. When every item of a choice list has exactly one non-blank indented line and that line is only a Markdown image, the list renders as a grid of tiles (`grid`, set in `parseList` in `mac/Sources/Byre/Markdown.swift`): four across, or two across when the context is at most 480pt wide. The tool's description tells agents to write image choices this way. A malformed call (no `context` or `why_blocked`, fewer than two choices, pick-one and pick-any choices mixed, a pick-one question without exactly one recommendation, two choices with the same text, an unknown attachment kind, a foreign `thread_id`) is rejected with a message saying what to fix, and nothing is filed. All questions are alike: the tool's description and its result tell the agent to end its turn after asking, and the answer arrives as its next message. When the task's worktree is behind the default branch (`git.Behind`: commits on the repo's local default branch that `HEAD` lacks), the result says by how many, and suggests merging it in while the human answers before ending the turn. The call returns the item's id as `question_id`. An agent withdraws a question it no longer needs answered (the human answered it in a chat message, say) with the `cancel_question` MCP tool and that id: an `open` question of its own session becomes `dismissed` with no `resolved_by` (its `inbox.updated` event has actor `agent`), which also stops it from holding back delivery; anything else is rejected with a message saying why. The inbox holds only questions; errors go in the transcript (see "Errors").
|
||||
|
||||
`GET /api/inbox` is one query over open items, oldest first; `project_id` and `task_id` narrow it to a project or task view, and `state` picks another state. The right pane shows a task's questions in its transcript, where they were asked, and they are answered there (below).
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"time"
|
||||
|
||||
"g.fc.run/ai/byre/internal/api"
|
||||
"g.fc.run/ai/byre/internal/git"
|
||||
"g.fc.run/ai/byre/internal/mcp"
|
||||
"g.fc.run/ai/byre/internal/store"
|
||||
)
|
||||
@@ -162,8 +163,24 @@ func (m *Manager) ask(ctx context.Context, l *live, raw json.RawMessage) (string
|
||||
slog.Error("answer question while away", "item", id, "user", creator.Login, "err", err)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("Question filed in the human's inbox. question_id: %s. End your turn now. The answer will arrive as your next message. "+
|
||||
"If the human answers it some other way, such as in a chat message, withdraw it with cancel_question and question_id %q.", id, id), nil
|
||||
filed := fmt.Sprintf("Question filed in the human's inbox. question_id: %s. ", id)
|
||||
rest := fmt.Sprintf("The answer will arrive as your next message. "+
|
||||
"If the human answers it some other way, such as in a chat message, withdraw it with cancel_question and question_id %q.", id)
|
||||
|
||||
project, err := m.store.GetProject(ctx, task.ProjectId)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
behind, err := git.Behind(task.WorktreePath, project.DefaultBranch)
|
||||
if err != nil {
|
||||
slog.Error("count commits behind default branch", "task", task.Id, "err", err)
|
||||
}
|
||||
if behind > 0 {
|
||||
return filed + fmt.Sprintf("This worktree is %d commits behind %s. While the human answers is a good time to merge %s in: "+
|
||||
"commit your work if the tree isn't clean, run `git merge %s`, resolve any conflicts, and check the result. Then end your turn. ",
|
||||
behind, project.DefaultBranch, project.DefaultBranch, project.DefaultBranch) + rest, nil
|
||||
}
|
||||
return filed + "End your turn now. " + rest, nil
|
||||
}
|
||||
|
||||
type cancelArgs struct {
|
||||
|
||||
@@ -177,6 +177,14 @@ func UnmergedChanges(worktree, target string) (Unmerged, error) {
|
||||
return u, nil
|
||||
}
|
||||
|
||||
func Behind(worktree, target string) (int, error) {
|
||||
count, err := run(worktree, "rev-list", "--count", "HEAD.."+target)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return strconv.Atoi(count)
|
||||
}
|
||||
|
||||
func SquashCommit(repo, worktree, target, message string, env []string, sign func(payload []byte) ([]byte, error)) (string, error) {
|
||||
checkedOut, err := run(repo, "symbolic-ref", "--short", "HEAD")
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user