Nudge workflow stages that end a turn without finish_stage
This commit is contained in:
@@ -11,6 +11,7 @@ import (
|
||||
"g.fc.run/ai/byre/internal/api"
|
||||
"g.fc.run/ai/byre/internal/mcp"
|
||||
"g.fc.run/ai/byre/internal/store"
|
||||
"g.fc.run/ai/byre/internal/workflow"
|
||||
)
|
||||
|
||||
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. " +
|
||||
@@ -24,6 +25,17 @@ const readyPromptUnchanged = "Your turn has ended, and this task's branch has no
|
||||
"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
|
||||
|
||||
func finishStagePrompt(frozen store.FrozenWorkflow, stage string) string {
|
||||
waiting := "the workflow can't finish"
|
||||
next := workflow.Next(frozen.Definition, stage)
|
||||
if len(next) > 0 {
|
||||
waiting = fmt.Sprintf("%s can't start", quoteList(next))
|
||||
}
|
||||
return fmt.Sprintf("Your turn has ended and the review of your latest work has finished, but you haven't called finish_stage, so stage %q of workflow %q hasn't finished and %s. "+
|
||||
"If the stage's work is done, call finish_stage with its output, then end your turn with one line. If work remains, do it now and then call finish_stage. "+
|
||||
"If you're waiting on the human for a decision or input, ask it with ask_human and end your turn.", stage, frozen.Name, waiting)
|
||||
}
|
||||
|
||||
type readyArgs struct {
|
||||
Overview string `json:"overview"`
|
||||
Merge string `json:"merge"`
|
||||
@@ -157,11 +169,23 @@ func (m *Manager) tryPromptReady(ctx context.Context, taskID string, fromReview
|
||||
if task.Status != api.TaskStatusActive || !readyState(task.State) {
|
||||
return nil
|
||||
}
|
||||
stagePrompt := ""
|
||||
if task.WorkflowStage != nil {
|
||||
finished, err := m.store.WorkflowFinished(ctx, *task.WorkflowRun)
|
||||
if err != nil || !finished {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !finished {
|
||||
output, _, err := m.store.StageResult(ctx, taskID)
|
||||
if err != nil || output != "" {
|
||||
return err
|
||||
}
|
||||
frozen, err := m.store.TaskWorkflow(ctx, taskID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
stagePrompt = finishStagePrompt(frozen, *task.WorkflowStage)
|
||||
}
|
||||
}
|
||||
sess, err = m.store.GetSession(ctx, sess.Id)
|
||||
if err != nil || sess.State == api.AgentSessionStateRunning || m.interrupted(sess.Id) {
|
||||
@@ -215,6 +239,9 @@ func (m *Manager) tryPromptReady(ctx context.Context, taskID string, fromReview
|
||||
if len(reviews) == 1 && reviews[0].State == api.ReviewStateSkipped {
|
||||
prompt = readyPromptUnchanged
|
||||
}
|
||||
if stagePrompt != "" {
|
||||
prompt = stagePrompt
|
||||
}
|
||||
_, err = m.Send(ctx, sess.Id, "byre", prompt, "", "")
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -822,7 +822,11 @@ func (s *Store) ReadyPromptDue(ctx context.Context, id string) (bool, error) {
|
||||
WHERE t.id = ? AND e.task_id = t.id AND e.seq > t.ready_prompt_seq
|
||||
AND ((e.kind = 'snapshot.created' AND json_extract(e.payload, '$.trigger') NOT IN (?, ?, ?))
|
||||
OR (e.kind = 'user.message' AND e.actor != ? AND e.actor NOT LIKE '% (lead)' AND json_extract(e.payload, '$.bounce') IS NOT 1))
|
||||
)`, id, string(api.SnapshotTriggerAnswer), string(api.SnapshotTriggerFork), string(api.SnapshotTriggerAskHuman), agentActor).Scan(&due)
|
||||
) OR EXISTS (
|
||||
SELECT 1 FROM events e, tasks t, tasks r
|
||||
WHERE t.id = ? AND t.workflow_run != '' AND r.workflow_run = t.workflow_run AND e.task_id = r.id
|
||||
AND e.kind = 'task.workflow_finished' AND e.seq > t.ready_prompt_seq
|
||||
)`, id, string(api.SnapshotTriggerAnswer), string(api.SnapshotTriggerFork), string(api.SnapshotTriggerAskHuman), agentActor, id).Scan(&due)
|
||||
return due, err
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user