Fold task state into one server field; add ready cancel and dismiss
This commit is contained in:
@@ -138,7 +138,7 @@ final class ChatModel {
|
||||
|
||||
var reviewing: Bool {
|
||||
let state = taskId.flatMap { store.findTask($0) }?.state
|
||||
return state == .reviewing || state == .reviewPending
|
||||
return state == .reviewing || state == .reviewPending || state == .mergeAfterReview
|
||||
}
|
||||
|
||||
var subagents: Bool {
|
||||
@@ -1416,6 +1416,8 @@ private struct ReadyBubble: View {
|
||||
let latest: Bool
|
||||
let unmerged: Bool
|
||||
@Environment(Store.self) private var store
|
||||
@Environment(Overlays.self) private var overlays
|
||||
@ViewState private var dismissing = false
|
||||
|
||||
var body: some View {
|
||||
let task = taskId.flatMap { store.findTask($0) }
|
||||
@@ -1434,6 +1436,20 @@ private struct ReadyBubble: View {
|
||||
let busy = store.taskBusy(task.id)
|
||||
HStack(spacing: 8) {
|
||||
Spacer(minLength: 0)
|
||||
Button("Dismiss") {
|
||||
dismissing = true
|
||||
Task {
|
||||
do {
|
||||
_ = try await store.api.client.dismissReady(path: .init(taskId: task.id)).ok
|
||||
} catch {
|
||||
overlays.showAlert(errorText(error))
|
||||
}
|
||||
dismissing = false
|
||||
}
|
||||
}
|
||||
.buttonStyle(SmallLinkButtonStyle())
|
||||
.disabled(dismissing)
|
||||
.help(discard ? "Drop this suggestion; the task stays open" : "Drop this ready call; the task stays open")
|
||||
if !discard {
|
||||
MergeButton(task: task, busy: busy, unmerged: unmerged, small: true)
|
||||
}
|
||||
|
||||
@@ -33,11 +33,21 @@ struct TaskState {
|
||||
}
|
||||
}
|
||||
|
||||
private let workingTitles: [ByreTask.StatePayload: (label: String, title: String)] = [
|
||||
.running: ("Working", "The agent is mid-turn or its subagents are running"),
|
||||
.reviewPending: ("Reviewing", "A review of the turn's work is about to start"),
|
||||
.reviewing: ("Reviewing", "A review is running"),
|
||||
.discarding: ("Discarding", "The agent is writing the task summary, then byre removes the task"),
|
||||
private let stateTitles: [ByreTask.StatePayload: TaskState] = [
|
||||
.asking: TaskState(kind: .answer, label: "Needs your answer", title: "The agent asked a question"),
|
||||
.merging: TaskState(kind: .merging, label: "Merging", title: "The merge is under way"),
|
||||
.mergeHeld: TaskState(kind: .held, label: "Merge held", title: "Waiting for the merge in progress in this project to finish"),
|
||||
.mergeAfterReview: TaskState(kind: .held, label: "Merge after review", title: "Merges when the running review finishes with no findings"),
|
||||
.running: TaskState(kind: .working, label: "Working", title: "The agent is mid-turn or its subagents are running"),
|
||||
.reviewPending: TaskState(kind: .working, label: "Reviewing", title: "A review of the turn's work is about to start"),
|
||||
.reviewing: TaskState(kind: .working, label: "Reviewing", title: "A review is running"),
|
||||
.discarding: TaskState(kind: .working, label: "Discarding", title: "The agent is writing the task summary, then byre removes the task"),
|
||||
.readyToMerge: TaskState(kind: .ready, label: "Ready to merge", title: "The agent says the task is ready to merge"),
|
||||
.discardSuggested: TaskState(kind: .ready, label: "Ready to discard", title: "The agent suggests discarding the task"),
|
||||
.idle: TaskState(kind: .idle, label: "Idle", title: "Waiting for a message"),
|
||||
.noAgent: TaskState(kind: .idle, label: "Idle", title: "Waiting for a message"),
|
||||
.merged: TaskState(kind: .idle, label: "Merged", title: "The task was merged"),
|
||||
.discarded: TaskState(kind: .idle, label: "Discarded", title: "The task was discarded"),
|
||||
]
|
||||
|
||||
@MainActor
|
||||
@@ -207,44 +217,12 @@ final class Store {
|
||||
}
|
||||
|
||||
func taskState(_ task: ByreTask) -> TaskState {
|
||||
if inbox.contains(where: { $0.taskId == task.id }) {
|
||||
return TaskState(kind: .answer, label: "Needs your answer", title: "The agent asked a question")
|
||||
}
|
||||
let busy = progress(task)
|
||||
if let busy, busy.kind == .merging {
|
||||
return busy
|
||||
}
|
||||
if task.mergeQueuedAt != nil {
|
||||
return TaskState(kind: .held, label: "Merge held", title: "Waiting for the merge in progress in this project to finish")
|
||||
}
|
||||
if task.mergeAfterReview {
|
||||
return TaskState(kind: .held, label: "Merge after review", title: "Merges when the running review finishes with no findings")
|
||||
}
|
||||
if let busy {
|
||||
return busy
|
||||
}
|
||||
switch task.ready {
|
||||
case .merge:
|
||||
return TaskState(kind: .ready, label: "Ready to merge", title: "The agent says the task is ready to merge")
|
||||
case .discard:
|
||||
return TaskState(kind: .ready, label: "Ready to discard", title: "The agent suggests discarding the task")
|
||||
case nil:
|
||||
break
|
||||
}
|
||||
return TaskState(kind: .idle, label: "Idle", title: "Waiting for a message")
|
||||
stateTitles[task.state]!
|
||||
}
|
||||
|
||||
func progress(_ task: ByreTask) -> TaskState? {
|
||||
if task.merging {
|
||||
return TaskState(kind: .merging, label: "Merging", title: "The merge turn is running")
|
||||
}
|
||||
if task.mergingIntoParent {
|
||||
return TaskState(kind: .merging, label: "Merging", title: "Waiting for the parent task's agent to merge it")
|
||||
}
|
||||
if let working = workingTitles[task.state] {
|
||||
return TaskState(kind: .working, label: working.label, title: working.title)
|
||||
}
|
||||
return nil
|
||||
let state = taskState(task)
|
||||
return state.kind == .merging || state.kind == .working ? state : nil
|
||||
}
|
||||
|
||||
func taskRank(_ task: ByreTask) -> Int {
|
||||
@@ -634,7 +612,7 @@ final class Store {
|
||||
spawn("load projects") {
|
||||
try await self.loadProjects()
|
||||
}
|
||||
case "task.created", "task.merge_title", "task.merge_queued", "task.merge_unqueued", "task.merge_after_review", "task.merge_after_review_ended", "task.merge_after_review_cancelled", "task.parent_merge_started", "task.parent_merge_stopped", "task.merge_started", "task.merge_ended", "task.discard_started", "task.merged", "task.discarded", "task.state", "task.ready", "task.discard_suggested", "snapshot.created":
|
||||
case "task.created", "task.merge_title", "task.merge_queued", "task.merge_unqueued", "task.merge_after_review", "task.merge_after_review_ended", "task.merge_after_review_cancelled", "task.parent_merge_started", "task.parent_merge_stopped", "task.merge_started", "task.merge_ended", "task.discard_started", "task.merged", "task.discarded", "task.state", "task.ready", "task.discard_suggested", "task.ready_cleared", "snapshot.created":
|
||||
if let taskId = ev.taskId, ["task.merged", "task.discarded"].contains(ev.kind) || (ev.kind == "snapshot.created" && !["answer", "fork"].contains(str(ev.payload.value, "trigger"))) {
|
||||
clearTask(self, taskId)
|
||||
}
|
||||
|
||||
@@ -578,7 +578,7 @@ final class TranscriptBuilder {
|
||||
if (ev.kind == "task.ready" || ev.kind == "task.discard_suggested") && sink == nil {
|
||||
lastReady = row.id
|
||||
}
|
||||
if ev.kind == "inbox.created" && sink == nil {
|
||||
if (ev.kind == "inbox.created" || ev.kind == "task.ready_cleared") && sink == nil {
|
||||
lastReady = nil
|
||||
}
|
||||
put(row, keepHeld: ev.kind == "agent.thinking")
|
||||
@@ -801,6 +801,9 @@ final class TranscriptBuilder {
|
||||
case "task.discard_suggested":
|
||||
return Row(.ready(when: when, md: parseMarkdown(jsString(p["reason"])), discard: true), taskId: taskId)
|
||||
|
||||
case "task.ready_cleared":
|
||||
return note([.text(ev.actor == "agent" ? "the agent withdrew its ready call" : "\(ev.actor) dismissed the ready call")])
|
||||
|
||||
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