Run scheduled tasks through workflows

This commit is contained in:
Ian Gulliver
2026-10-22 13:38:40 -07:00
parent 9fca82abaa
commit 2524e1cb1f
9 changed files with 456 additions and 446 deletions
+13 -17
View File
@@ -85,7 +85,7 @@ struct ScheduleRows: View {
.font(Fonts.font(11))
.foregroundStyle(Palette.muted)
}
.help(sc.workflow.isEmpty ? "Runs one agent on \(sc.model)" : "Runs workflow \(sc.workflow)")
.help("Runs workflow \(sc.workflow)")
}
}
.padding(.bottom, 8)
@@ -102,12 +102,11 @@ private final class ScheduleDraft {
var minute: Int
var dayOfMonth: Int
var workflow: String
var model: String
var prompt: String
var workflows: [String] = []
var failure = ""
init(_ sc: Schedule?, defaultModel: String) {
init(_ sc: Schedule?) {
title = sc?.title ?? ""
every = sc?.every ?? .week
weekdays = Set(sc?.weekdays ?? [.mon])
@@ -115,12 +114,11 @@ private final class ScheduleDraft {
minute = sc?.minute ?? 0
dayOfMonth = sc.map { $0.dayOfMonth == 0 ? 1 : $0.dayOfMonth } ?? 1
workflow = sc?.workflow ?? ""
model = sc.map { $0.model.isEmpty ? defaultModel : $0.model } ?? defaultModel
prompt = sc?.prompt ?? ""
}
var valid: Bool {
!trimmed(title).isEmpty && (every != .week || !weekdays.isEmpty) && (!workflow.isEmpty || !trimmed(prompt).isEmpty)
!trimmed(title).isEmpty && (every != .week || !weekdays.isEmpty) && !workflow.isEmpty
}
var save: ScheduleSave {
@@ -132,17 +130,19 @@ private final class ScheduleDraft {
minute: minute,
dayOfMonth: every == .month ? dayOfMonth : nil,
prompt: prompt,
model: workflow.isEmpty ? model : nil,
workflow: workflow.isEmpty ? nil : workflow
workflow: workflow
)
}
}
@MainActor
func editSchedule(_ project: Project, _ existing: Schedule?, _ store: Store, _ overlays: Overlays) async {
let draft = ScheduleDraft(existing, defaultModel: store.defaultModel)
let draft = ScheduleDraft(existing)
Task {
draft.workflows = (try? await store.api.client.listWorkflows().ok.body.json.map(\.name)) ?? []
if draft.workflow.isEmpty, let first = draft.workflows.first {
draft.workflow = first
}
}
while true {
let buttons = existing == nil
@@ -183,10 +183,10 @@ private struct ScheduleEditor: View {
var body: some View {
VStack(alignment: .leading, spacing: 12) {
Text("Schedule a task in \(project.name), started from \(project.defaultBranch). Times are the server's.")
Text("Schedule a workflow run in \(project.name), started from \(project.defaultBranch). Times are the server's.")
.foregroundStyle(Palette.muted)
.fixedSize(horizontal: false, vertical: true)
TextField("Task title", text: $draft.title)
TextField("Title", text: $draft.title)
.modifier(FieldStyle())
HStack(alignment: .firstTextBaseline, spacing: 8) {
Text("Every").foregroundStyle(Palette.muted)
@@ -213,15 +213,11 @@ private struct ScheduleEditor: View {
}
}
HStack(alignment: .firstTextBaseline, spacing: 8) {
Text("Run").foregroundStyle(Palette.muted)
Text("Run workflow").foregroundStyle(Palette.muted)
let names = draft.workflow.isEmpty || draft.workflows.contains(draft.workflow) ? draft.workflows : [draft.workflow] + draft.workflows
ChoiceMenu(value: $draft.workflow, options: [("", "one agent")] + names.map { ($0, "workflow \($0)") })
if draft.workflow.isEmpty {
Text("on").foregroundStyle(Palette.muted)
ModelPicker(model: $draft.model)
}
ChoiceMenu(value: $draft.workflow, options: names.map { ($0, $0) })
}
Text(draft.workflow.isEmpty ? "The agent's first message: a complete brief, since it sees nothing else." : "The workflow's brief, given to every stage (optional).")
Text("The workflow's brief, given to every stage (optional).")
.foregroundStyle(Palette.muted)
.fixedSize(horizontal: false, vertical: true)
PromptEditor(text: $draft.prompt)