
























@@ -8,6 +8,8 @@ final class LiveActivityManager {
88static let shared = LiveActivityManager()
991010private let logger = Logger(subsystem: "ai.openclaw.ios", category: "LiveActivity")
11+private let connectingStaleSeconds: TimeInterval = 120
12+private let hydrationStaleSeconds: TimeInterval = 300
1113private var currentActivity: Activity<OpenClawActivityAttributes>?
1214private var activityStartDate: Date = .now
1315@@ -24,11 +26,11 @@ final class LiveActivityManager {
2426return true
2527}
262827-func startActivity(agentName: String, sessionKey: String) {
29+func showConnecting(statusText: String = "Connecting...", agentName: String, sessionKey: String) {
2830self.hydrateCurrentAndPruneDuplicates()
29313032if self.currentActivity != nil {
31-self.handleConnecting()
33+self.handleConnecting(statusText: statusText)
3234return
3335}
3436@@ -40,11 +42,14 @@ final class LiveActivityManager {
40424143self.activityStartDate = .now
4244let attributes = OpenClawActivityAttributes(agentName: agentName, sessionKey: sessionKey)
45+let state = self.connectingState(statusText: statusText)
43464447do {
4548let activity = try Activity.request(
4649 attributes: attributes,
47- content: ActivityContent(state: self.connectingState(), staleDate: nil),
50+ content: ActivityContent(
51+ state: state,
52+ staleDate: Date().addingTimeInterval(self.connectingStaleSeconds)),
4853 pushType: nil)
4954self.currentActivity = activity
5055self.logger.info("started live activity id=\(activity.id, privacy: .public)")
@@ -53,16 +58,57 @@ final class LiveActivityManager {
5358}
5459}
556056-func handleConnecting() {
57-self.updateCurrent(state: self.connectingState())
61+func showAttention(statusText: String, agentName: String, sessionKey: String) {
62+self.hydrateCurrentAndPruneDuplicates()
63+64+if self.currentActivity == nil {
65+let authInfo = ActivityAuthorizationInfo()
66+guard authInfo.areActivitiesEnabled else {
67+self.logger.info("Live Activities disabled; skipping attention state")
68+return
69+}
70+self.activityStartDate = .now
71+let attributes = OpenClawActivityAttributes(agentName: agentName, sessionKey: sessionKey)
72+do {
73+let activity = try Activity.request(
74+ attributes: attributes,
75+ content: ActivityContent(state: self.attentionState(statusText: statusText), staleDate: nil),
76+ pushType: nil)
77+self.currentActivity = activity
78+self.logger.info("started attention live activity id=\(activity.id, privacy: .public)")
79+} catch {
80+self.logger.error(
81+"failed to start attention live activity: \(error.localizedDescription, privacy: .public)")
82+}
83+return
84+}
85+86+self.updateCurrent(state: self.attentionState(statusText: statusText), staleDate: nil)
87+}
88+89+func handleConnecting(statusText: String = "Connecting...") {
90+self.updateCurrent(
91+ state: self.connectingState(statusText: statusText),
92+ staleDate: Date().addingTimeInterval(self.connectingStaleSeconds))
5893}
59946095func handleReconnect() {
61-self.updateCurrent(state: self.idleState())
96+self.endActivity(reason: "connected")
6297}
63986499func handleDisconnect() {
65-self.updateCurrent(state: self.disconnectedState())
100+self.endActivity(reason: "disconnected")
101+}
102+103+func endActivity(reason: String) {
104+guard let activity = self.currentActivity else { return }
105+self.currentActivity = nil
106+self.logger.info("ending live activity reason=\(reason, privacy: .public)")
107+Task {
108+await activity.end(
109+ActivityContent(state: self.disconnectedState(), staleDate: nil),
110+ dismissalPolicy: .immediate)
111+}
66112}
6711368114private func hydrateCurrentAndPruneDuplicates() {
@@ -72,39 +118,71 @@ final class LiveActivityManager {
72118return
73119}
7412075-let keeper = active.max { lhs, rhs in
121+let now = Date()
122+let candidates = active.filter { activity in
123+let state = activity.content.state
124+guard activity.activityState == .active else { return false }
125+guard !state.isIdle, !state.isDisconnected else { return false }
126+return now.timeIntervalSince(state.startedAt) < self.hydrationStaleSeconds
127+}
128+129+guard !candidates.isEmpty else {
130+self.currentActivity = nil
131+for activity in active {
132+self.end(activity: activity)
133+}
134+return
135+}
136+137+let keeper = candidates.max { lhs, rhs in
76138 lhs.content.state.startedAt < rhs.content.state.startedAt
77-} ?? active[0]
139+} ?? candidates[0]
7814079141self.currentActivity = keeper
80142self.activityStartDate = keeper.content.state.startedAt
8114382144let stale = active.filter { $0.id != keeper.id }
83145for activity in stale {
84-Task {
85-await activity.end(
86-ActivityContent(state: self.disconnectedState(), staleDate: nil),
87- dismissalPolicy: .immediate)
88-}
146+self.end(activity: activity)
89147}
90148}
9114992-private func updateCurrent(state: OpenClawActivityAttributes.ContentState) {
93-guard let activity = self.currentActivity else { return }
150+private func updateCurrent(state: OpenClawActivityAttributes.ContentState, staleDate: Date? = nil) {
151+guard let activity = self.currentActivity, activity.activityState == .active else {
152+self.currentActivity = nil
153+return
154+}
155+Task {
156+await activity.update(ActivityContent(state: state, staleDate: staleDate))
157+}
158+}
159+160+private func end(activity: Activity<OpenClawActivityAttributes>) {
94161Task {
95-await activity.update(ActivityContent(state: state, staleDate: nil))
162+await activity.end(
163+ActivityContent(state: self.disconnectedState(), staleDate: nil),
164+ dismissalPolicy: .immediate)
96165}
97166}
9816799-private func connectingState() -> OpenClawActivityAttributes.ContentState {
168+private func connectingState(statusText: String = "Connecting...") -> OpenClawActivityAttributes.ContentState {
100169OpenClawActivityAttributes.ContentState(
101- statusText: "Connecting...",
170+ statusText: statusText,
102171 isIdle: false,
103172 isDisconnected: false,
104173 isConnecting: true,
105174 startedAt: self.activityStartDate)
106175}
107176177+private func attentionState(statusText: String) -> OpenClawActivityAttributes.ContentState {
178+OpenClawActivityAttributes.ContentState(
179+ statusText: statusText,
180+ isIdle: false,
181+ isDisconnected: false,
182+ isConnecting: false,
183+ startedAt: self.activityStartDate)
184+}
185+108186private func idleState() -> OpenClawActivityAttributes.ContentState {
109187OpenClawActivityAttributes.ContentState(
110188 statusText: "Idle",
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。