Let task agents suggest discarding their task, with reasoning
This commit is contained in:
@@ -310,6 +310,7 @@ final class ChatModel {
|
||||
var questions: [Int] = []
|
||||
var open: [Int] = []
|
||||
var ready: [Int] = []
|
||||
var discard = false
|
||||
for row in builder.root.rows where !row.hidden {
|
||||
switch row.kind {
|
||||
case .question where row.question?.state == .open:
|
||||
@@ -318,8 +319,9 @@ final class ChatModel {
|
||||
if let r = row.review, r.id == latest?.id, findingOpen(r, i) {
|
||||
open.append(row.id)
|
||||
}
|
||||
case .ready where row.id == builder.lastReady && active:
|
||||
case .ready(_, _, let d) where row.id == builder.lastReady && active:
|
||||
ready.append(row.id)
|
||||
discard = d
|
||||
default:
|
||||
break
|
||||
}
|
||||
@@ -332,7 +334,9 @@ final class ChatModel {
|
||||
groups.append(AttentionGroup(id: "findings", label: open.count == 1 ? "1 finding" : "\(open.count) findings", help: "Scroll to the next finding of the latest review that nobody has answered", rows: open))
|
||||
}
|
||||
if !ready.isEmpty {
|
||||
groups.append(AttentionGroup(id: "ready", label: "ready to merge", help: "Scroll to the agent's ready-for-merge overview", rows: ready))
|
||||
groups.append(discard
|
||||
? AttentionGroup(id: "ready", label: "suggests discard", help: "Scroll to the agent's reasons to discard the task", rows: ready)
|
||||
: AttentionGroup(id: "ready", label: "ready to merge", help: "Scroll to the agent's ready-for-merge overview", rows: ready))
|
||||
}
|
||||
return groups
|
||||
}
|
||||
@@ -1257,8 +1261,8 @@ struct RowView: View {
|
||||
case .findingResponse(let snapshot, let index, let response, let reason):
|
||||
FindingResponseView(row: row, model: model, snapshot: snapshot, index: index, response: response, reason: reason)
|
||||
|
||||
case .ready(let when, let blocks):
|
||||
ReadyBubble(when: when, blocks: blocks, taskId: row.taskId, latest: row.id == model.builder.lastReady, unmerged: model.hasUnmerged)
|
||||
case .ready(let when, let blocks, let discard):
|
||||
ReadyBubble(when: when, blocks: blocks, discard: discard, taskId: row.taskId, latest: row.id == model.builder.lastReady, unmerged: model.hasUnmerged)
|
||||
|
||||
case .handoff:
|
||||
DetailsBox(boxed: false) {
|
||||
@@ -1388,6 +1392,7 @@ private struct FindingResponseView: View {
|
||||
private struct ReadyBubble: View {
|
||||
let when: Date
|
||||
let blocks: [MDBlock]
|
||||
let discard: Bool
|
||||
let taskId: String?
|
||||
let latest: Bool
|
||||
let unmerged: Bool
|
||||
@@ -1398,7 +1403,7 @@ private struct ReadyBubble: View {
|
||||
let current = latest && task?.status == .active
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
HStack(spacing: 8) {
|
||||
Text("ready for merge")
|
||||
Text(discard ? "suggests discarding" : "ready for merge")
|
||||
.font(Fonts.font(11, weight: current ? 700 : 400))
|
||||
.foregroundStyle(current ? Palette.warn : Palette.muted)
|
||||
WhenText(when: when)
|
||||
@@ -1410,7 +1415,9 @@ private struct ReadyBubble: View {
|
||||
let busy = store.taskBusy(task.id)
|
||||
HStack(spacing: 8) {
|
||||
Spacer(minLength: 0)
|
||||
MergeButton(task: task, busy: busy, unmerged: unmerged, small: true)
|
||||
if !discard {
|
||||
MergeButton(task: task, busy: busy, unmerged: unmerged, small: true)
|
||||
}
|
||||
DiscardButton(task: task, busy: busy, small: true)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ enum RowKind {
|
||||
case review(note: Note, md: [MDBlock])
|
||||
case finding(index: Int)
|
||||
case findingResponse(snapshot: Int, index: Int, response: String, reason: String)
|
||||
case ready(when: Date, md: [MDBlock])
|
||||
case ready(when: Date, md: [MDBlock], discard: Bool)
|
||||
case handoff
|
||||
case snapshot(Snapshot)
|
||||
case question
|
||||
@@ -575,7 +575,7 @@ final class TranscriptBuilder {
|
||||
if ev.kind == "task.merge_after_review" && sink == nil {
|
||||
lastMergeAfterReview = row.id
|
||||
}
|
||||
if ev.kind == "task.ready" && sink == nil {
|
||||
if (ev.kind == "task.ready" || ev.kind == "task.discard_suggested") && sink == nil {
|
||||
lastReady = row.id
|
||||
}
|
||||
if ev.kind == "inbox.created" && sink == nil {
|
||||
@@ -796,7 +796,10 @@ final class TranscriptBuilder {
|
||||
return note([.text("\(ev.actor) set the task summary")])
|
||||
|
||||
case "task.ready":
|
||||
return Row(.ready(when: when, md: parseMarkdown(jsString(p["overview"]))), taskId: taskId)
|
||||
return Row(.ready(when: when, md: parseMarkdown(jsString(p["overview"])), discard: false), taskId: taskId)
|
||||
|
||||
case "task.discard_suggested":
|
||||
return Row(.ready(when: when, md: parseMarkdown(jsString(p["reason"])), discard: true), taskId: taskId)
|
||||
|
||||
case "task.discard_started":
|
||||
return note([.text("\(ev.actor) asked to discard the task; the agent writes the task summary first")])
|
||||
|
||||
Reference in New Issue
Block a user