forked from BasedHardware/omi
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathRecurringTaskScheduler.swift
More file actions
86 lines (73 loc) · 2.98 KB
/
Copy pathRecurringTaskScheduler.swift
File metadata and controls
86 lines (73 loc) · 2.98 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
import Foundation
/// Checks every 60 seconds for recurring tasks that are due and triggers
/// AI chat investigations for each one via TaskChatCoordinator (agent bridge).
/// Dedup is automatic — investigateInBackground skips tasks with existing messages.
@MainActor
class RecurringTaskScheduler {
static let shared = RecurringTaskScheduler()
private var timer: Timer?
private var coordinator: TaskChatCoordinator?
private init() {}
/// Wire the canonical coordinator from `ViewModelContainer` before `start()`.
func configure(taskChatCoordinator: TaskChatCoordinator) {
coordinator = taskChatCoordinator
}
func start() {
guard coordinator != nil else {
log("RecurringTaskScheduler: taskChatCoordinator not configured — skipping start")
return
}
guard timer == nil else { return }
log("RecurringTaskScheduler: Starting (60s interval)")
timer = Timer.scheduledTimer(withTimeInterval: 60, repeats: true) { [weak self] _ in
Task { @MainActor in
await self?.checkDueTasks()
}
}
// Also run immediately on start
Task { await checkDueTasks() }
}
func stop() {
timer?.invalidate()
timer = nil
log("RecurringTaskScheduler: Stopped")
}
private func checkDueTasks() async {
guard let coordinator else { return }
guard AuthState.shared.isSignedIn else { return }
guard TaskAgentSettings.shared.isChatEnabled else { return }
guard let tasks = try? await ActionItemStorage.shared.getDueRecurringTasks(),
!tasks.isEmpty
else { return }
log("RecurringTaskScheduler: Found \(tasks.count) due recurring task(s)")
// Separate daily tasks for special handling
let dailyTasks = tasks.filter { $0.recurrenceRule == "daily" }
let otherTasks = tasks.filter { $0.recurrenceRule != "daily" }
// Handle daily tasks with lighter touch - just check if investigation already exists
for task in dailyTasks {
// Only investigate if no recent chat session exists
// (can't use || with await because the rhs is an @autoclosure)
let needsInvestigation: Bool
if task.chatSessionId == nil {
needsInvestigation = true
} else {
needsInvestigation = await shouldReinvestigateDaily(task: task)
}
if needsInvestigation {
await coordinator.investigateInBackground(for: task)
}
}
// Handle other recurring tasks normally
for task in otherTasks {
await coordinator.investigateInBackground(for: task)
}
}
/// Check if a daily task should be re-investigated (less frequent than other tasks)
private func shouldReinvestigateDaily(task: TaskActionItem) async -> Bool {
// For daily tasks, only re-investigate if more than 4 hours have passed
// to avoid overwhelming the user with daily task investigations
guard let lastInvestigation = task.agentStartedAt else { return true }
let hoursSinceLastInvestigation = Date().timeIntervalSince(lastInvestigation) / 3600
return hoursSinceLastInvestigation > 4
}
}