Load only active tasks on connect and refetch single tasks on events

This commit is contained in:
Ian Gulliver
2026-10-22 11:43:35 -07:00
parent 78fcdfff34
commit 48741e8cbb
5 changed files with 186 additions and 132 deletions
+5 -2
View File
@@ -337,10 +337,13 @@ paths:
parameters:
- { name: projectId, in: path, required: true, schema: { type: string } }
get:
operationId: listTasks
operationId: listActiveTasks
description: >
The project's active tasks. A closed task comes from getTask; task events name the task they change, which
clients fetch again with getTask.
responses:
"200":
description: The project's tasks, newest first.
description: The project's active tasks, newest first.
content:
application/json:
schema:
+92 -91
View File
@@ -2168,7 +2168,7 @@ type ServerInterface interface {
CreateSchedule(w http.ResponseWriter, r *http.Request, projectId string)
// (GET /api/projects/{projectId}/tasks)
ListTasks(w http.ResponseWriter, r *http.Request, projectId string)
ListActiveTasks(w http.ResponseWriter, r *http.Request, projectId string)
// (POST /api/projects/{projectId}/tasks)
CreateTask(w http.ResponseWriter, r *http.Request, projectId string)
@@ -3199,8 +3199,8 @@ func (siw *ServerInterfaceWrapper) CreateSchedule(w http.ResponseWriter, r *http
handler.ServeHTTP(w, r)
}
// ListTasks operation middleware
func (siw *ServerInterfaceWrapper) ListTasks(w http.ResponseWriter, r *http.Request) {
// ListActiveTasks operation middleware
func (siw *ServerInterfaceWrapper) ListActiveTasks(w http.ResponseWriter, r *http.Request) {
var err error
_ = err
@@ -3215,7 +3215,7 @@ func (siw *ServerInterfaceWrapper) ListTasks(w http.ResponseWriter, r *http.Requ
}
handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
siw.Handler.ListTasks(w, r, projectId)
siw.Handler.ListActiveTasks(w, r, projectId)
}))
for _, middleware := range siw.HandlerMiddlewares {
@@ -4756,7 +4756,7 @@ func HandlerWithOptions(si ServerInterface, options StdHTTPServerOptions) http.H
m.HandleFunc(http.MethodGet+" "+options.BaseURL+"/api/projects/{projectId}", wrapper.GetProject)
m.HandleFunc(http.MethodPut+" "+options.BaseURL+"/api/projects/{projectId}/review-workflow", wrapper.SetReviewWorkflow)
m.HandleFunc(http.MethodPut+" "+options.BaseURL+"/api/projects/{projectId}/audit-workflow", wrapper.SetAuditWorkflow)
m.HandleFunc(http.MethodGet+" "+options.BaseURL+"/api/projects/{projectId}/tasks", wrapper.ListTasks)
m.HandleFunc(http.MethodGet+" "+options.BaseURL+"/api/projects/{projectId}/tasks", wrapper.ListActiveTasks)
m.HandleFunc(http.MethodPost+" "+options.BaseURL+"/api/projects/{projectId}/tasks", wrapper.CreateTask)
m.HandleFunc(http.MethodGet+" "+options.BaseURL+"/api/projects/{projectId}/schedules", wrapper.ListSchedules)
m.HandleFunc(http.MethodPost+" "+options.BaseURL+"/api/projects/{projectId}/schedules", wrapper.CreateSchedule)
@@ -5095,92 +5095,93 @@ var swaggerSpec = []string{
"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",
"iEyh7ecrYTUicRMmooABvgNm8qtRUn/48P8PAA==",
}
// decodeSpec returns the embedded OpenAPI spec as raw JSON bytes,
+2 -2
View File
@@ -345,14 +345,14 @@ func (s *Server) GetProject(w http.ResponseWriter, r *http.Request, projectID st
writeJSON(w, http.StatusOK, p)
}
func (s *Server) ListTasks(w http.ResponseWriter, r *http.Request, projectID string) {
func (s *Server) ListActiveTasks(w http.ResponseWriter, r *http.Request, projectID string) {
_, err := s.store.GetProject(r.Context(), projectID)
if err != nil {
writeStoreError(w, err)
return
}
tasks, err := s.store.ListTasks(r.Context(), projectID)
tasks, err := s.store.ListActiveTasks(r.Context(), projectID)
if err != nil {
writeStoreError(w, err)
return
+4
View File
@@ -410,6 +410,10 @@ func (s *Store) ListTasks(ctx context.Context, projectID string) ([]api.Task, er
return s.queryTasks(ctx, `SELECT `+taskColumns+` FROM tasks WHERE project_id = ? ORDER BY created_at DESC`, projectID)
}
func (s *Store) ListActiveTasks(ctx context.Context, projectID string) ([]api.Task, error) {
return s.queryTasks(ctx, `SELECT `+taskColumns+` FROM tasks WHERE project_id = ? AND status = ? ORDER BY created_at DESC`, projectID, string(api.TaskStatusActive))
}
func (s *Store) GetTask(ctx context.Context, id string) (api.Task, error) {
t, err := scanTask(s.db.QueryRowContext(ctx, `SELECT `+taskColumns+` FROM tasks WHERE id = ?`, id))
if errors.Is(err, sql.ErrNoRows) {
+83 -37
View File
@@ -97,7 +97,8 @@ final class Store {
@ObservationIgnored private var historyLoads: Set<ObjectIdentifier> = []
@ObservationIgnored private var requestSeqs: [String: Int] = [:]
@ObservationIgnored private var taskReloads: Set<String> = []
@ObservationIgnored private var taskReloadAgain: Set<String> = []
@ObservationIgnored private var listReloads: Set<String> = []
@ObservationIgnored private var taskRefetches: [String: Set<String>] = [:]
@ObservationIgnored private var terminalChanges = 0
@ObservationIgnored private var streamOpens = 0
@ObservationIgnored private var globalStream: EventStream?
@@ -368,12 +369,70 @@ final class Store {
}
func loadTasks(_ projectId: String) async throws {
let latest = claimRequest("tasks:\(projectId)")
let list = try await api.client.listTasks(path: .init(projectId: projectId)).ok.body.json
if !latest() {
try await reloadTasks(projectId, list: true)
}
// Loads run one at a time per project, so a slower, older response never lands over a newer one.
private func reloadTasks(_ projectId: String, list: Bool = false, task: String? = nil) async throws {
if list {
listReloads.insert(projectId)
}
if let task {
taskRefetches[projectId, default: []].insert(task)
}
guard taskReloads.insert(projectId).inserted else {
return
}
tasks[projectId] = list
defer {
taskReloads.remove(projectId)
}
var failure: (any Error)?
while listReloads.contains(projectId) || taskRefetches[projectId] != nil {
do {
if listReloads.remove(projectId) != nil {
try await loadActiveTasks(projectId)
} else {
try await refetchTasks(projectId, taskRefetches.removeValue(forKey: projectId)!)
}
} catch {
failure = error
}
}
if let failure {
throw failure
}
}
private func loadActiveTasks(_ projectId: String) async throws {
let list = try await api.client.listActiveTasks(path: .init(projectId: projectId)).ok.body.json
let fresh = Set(list.map(\.id))
let held = (tasks[projectId] ?? []).filter { !fresh.contains($0.id) }
for t in held where t.status == .active {
taskRefetches[projectId, default: []].insert(t.id)
}
try await applyTasks(projectId, list + held)
}
private func refetchTasks(_ projectId: String, _ ids: Set<String>) async throws {
let api = api
let fetched = try await withThrowingTaskGroup(of: ByreTask.self) { group in
for id in ids {
group.addTask {
try await api.client.getTask(path: .init(taskId: id)).ok.body.json
}
}
var out: [ByreTask] = []
for try await t in group {
out.append(t)
}
return out
}
let fresh = Set(fetched.map(\.id))
try await applyTasks(projectId, fetched + (tasks[projectId] ?? []).filter { !fresh.contains($0.id) })
}
private func applyTasks(_ projectId: String, _ list: [ByreTask]) async throws {
tasks[projectId] = list.sorted { $0.createdAt > $1.createdAt }
for t in list where t.status == .active && !t.background {
_ = taskFeed(t.id)
}
@@ -384,26 +443,15 @@ final class Store {
}
}
private func reloadTasks(_ projectId: String) async throws {
guard taskReloads.insert(projectId).inserted else {
taskReloadAgain.insert(projectId)
private func lookUpTask(_ taskId: String) {
if findTask(taskId) != nil {
return
}
defer {
taskReloads.remove(projectId)
taskReloadAgain.remove(projectId)
}
var failure: (any Error)?
repeat {
taskReloadAgain.remove(projectId)
do {
try await loadTasks(projectId)
} catch {
failure = error
spawn("load task") {
let t = try await self.api.client.getTask(path: .init(taskId: taskId)).ok.body.json
if self.findTask(taskId) == nil {
self.tasks[t.projectId] = ((self.tasks[t.projectId] ?? []) + [t]).sorted { $0.createdAt > $1.createdAt }
}
} while taskReloadAgain.contains(projectId)
if let failure {
throw failure
}
}
@@ -674,6 +722,7 @@ final class Store {
markVisited(taskId)
selectedTaskId = taskId
if let taskId {
lookUpTask(taskId)
_ = taskFeed(taskId)
}
pinned = nil
@@ -798,11 +847,7 @@ final class Store {
forwards.close(taskId)
}
notifyTask(self, ev)
if let projectId = ev.projectId {
spawn("load tasks") {
try await self.reloadTasks(projectId)
}
}
reloadTask(ev)
case "session.created", "session.state", "session.subagents", "session.model":
if ev.owner != nil {
spawn("load lead sessions") {
@@ -821,11 +866,7 @@ final class Store {
spawn("load inbox") {
try await self.loadInbox()
}
if let projectId = ev.projectId {
spawn("load tasks") {
try await self.reloadTasks(projectId)
}
}
reloadTask(ev)
case "inbox.updated":
clearInbox(try! decodePayload(ev.payload, as: InboxItem.self))
spawn("load inbox") {
@@ -839,11 +880,7 @@ final class Store {
}
case "session.error", "session.rate_limited":
notifyError(self, ev)
if let projectId = ev.projectId {
spawn("load tasks") {
try await self.reloadTasks(projectId)
}
}
reloadTask(ev)
case "task.merge_facts":
if let projectId = ev.projectId, let taskId = ev.taskId, let i = tasks[projectId]?.firstIndex(where: { $0.id == taskId }) {
tasks[projectId]![i].mergeFacts = try! decodePayload(ev.payload, as: MergeFacts.self)
@@ -876,6 +913,15 @@ final class Store {
events.send(ev)
}
private func reloadTask(_ ev: ByreEvent) {
guard let projectId = ev.projectId, let taskId = ev.taskId else {
return
}
spawn("load task") {
try await self.reloadTasks(projectId, task: taskId)
}
}
private func updateTask(_ id: String, _ change: (inout ByreTask) -> Void) {
for (projectId, list) in tasks {
if let i = list.firstIndex(where: { $0.id == id }) {