Fold task state into one server field; add ready cancel and dismiss

This commit is contained in:
Ian Gulliver
2026-10-20 08:51:32 -07:00
parent 224a0d5a4b
commit e7724977c8
12 changed files with 506 additions and 388 deletions
+37 -4
View File
@@ -13,10 +13,16 @@ import (
"g.fc.run/ai/byre/internal/store"
)
const readyPromptAsk = "If it isn't ready (work remains that you mean to do, or you're waiting on the human), call neither: say what's left in one line and end your turn. " +
"If you're waiting on the human for a decision or input, ask it with ask_human now, even if an earlier message already put it in prose, so it reaches their inbox; only an open ask_human question already counts as asked. Don't change any files this turn."
const readyPrompt = "Your turn has ended and the review of your latest work has finished. If the task is ready to merge, call ready_for_merge with an overview of the whole task, then end your turn with one line. " +
"If the work shouldn't land at all (the approach failed, the task turned out unnecessary or already done), call suggest_discard with your reasoning instead, then end your turn with one line. " +
"If it isn't ready (work remains that you mean to do, or you're waiting on the human), call neither: say what's left in one line and end your turn. " +
"If you're waiting on the human for a decision or input, ask it with ask_human now, even if an earlier message already put it in prose, so it reaches their inbox; only an open ask_human question already counts as asked. Don't change any files this turn."
readyPromptAsk
const readyPromptUnchanged = "Your turn has ended, and this task's branch has no changes from the default branch, so there is nothing to merge. " +
"If the task is finished, or shouldn't go further (it needed no changes, the approach failed, or it was already done), call suggest_discard with your reasoning, then end your turn with one line. " +
readyPromptAsk
type readyArgs struct {
Overview string `json:"overview"`
@@ -97,6 +103,29 @@ func (m *Manager) suggestDiscardTool(l *live) mcp.Tool {
}
}
func (m *Manager) cancelReadyTool(l *live) mcp.Tool {
return mcp.Tool{
Name: "cancel_ready",
Description: "Withdraw your standing ready_for_merge or suggest_discard, when it no longer holds: for example the human raised a problem, or you found more work. " +
"The human then no longer sees the task as ready to merge or as suggested for discard; call ready_for_merge or suggest_discard again once it holds.",
InputSchema: json.RawMessage(`{"type": "object", "properties": {}}`),
Handler: func(ctx context.Context, raw json.RawMessage) (string, error) {
err := m.store.ClearReady(ctx, l.taskID, agentActor)
if errors.Is(err, store.ErrNotFound) {
return "", fmt.Errorf("%w: this task has no standing ready_for_merge or suggest_discard", ErrConflict)
}
if err != nil {
return "", err
}
return "Withdrawn: the human no longer sees this task as ready to merge or suggested for discard.", nil
},
}
}
func readyState(s api.TaskState) bool {
return s == api.TaskStateIdle || s == api.TaskStateReadyToMerge || s == api.TaskStateDiscardSuggested
}
func (m *Manager) promptReady(taskID string, from any) {
ctx := context.Background()
err := m.tryPromptReady(ctx, taskID, from == string(api.TaskStateReviewing))
@@ -117,7 +146,7 @@ func (m *Manager) tryPromptReady(ctx context.Context, taskID string, fromReview
if err != nil {
return err
}
if task.Status != api.TaskStatusActive || task.State != api.TaskStateIdle || task.MergeAfterReview {
if task.Status != api.TaskStatusActive || !readyState(task.State) {
return nil
}
if task.WorkflowStage != nil {
@@ -164,7 +193,11 @@ func (m *Manager) tryPromptReady(ctx context.Context, taskID string, fromReview
if err != nil || len(open) > 0 {
return err
}
_, err = m.Send(ctx, sess.Id, "byre", readyPrompt, "")
prompt := readyPrompt
if review.State == api.ReviewStateSkipped {
prompt = readyPromptUnchanged
}
_, err = m.Send(ctx, sess.Id, "byre", prompt, "")
if err != nil {
return err
}