mirror of
https://github.com/awizemann/scarf.git
synced 2026-05-10 10:36:35 +00:00
M7 #4: split isAgentWorking into isGenerating + isPostProcessing
Pass-1 showed the "Agent is working…" spinner persisting long after the reply had landed in the message list — Hermes delays the ACP `promptComplete` event while it does auxiliary post-work (title generation, usage accounting). Spinner stuck ~minute+ on a 2-second response. Fix without touching the ACP state machine: derive two computed properties from existing signals in RichChatViewModel: - `isGenerating`: agent is working AND we don't yet have a finalized assistant reply on the message list. Drives the prominent spinner. - `isPostProcessing`: agent is working AND the user CAN see the reply. Drives a subtle "Finishing up…" pill instead of the big spinner. When `promptComplete` finally arrives, `isAgentWorking` flips false and both derived props go quiet. `isAgentWorking` remains the canonical ACP-level flag (kept public for any consumer that really wants the raw value), just no longer the signal for visible "spinner now" UX. Applied to: - ScarfGo ChatView.swift — primary spinner + post-processing pill. - Mac RichChatView.swift — SessionInfoBar + RichChatMessageList now take `isGenerating` instead of `isAgentWorking`. Same UX win for the macOS app (pass-1 finding was cross-platform, just surfaced first on iOS). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -44,11 +44,41 @@ public final class RichChatViewModel {
|
||||
public var messages: [HermesMessage] = []
|
||||
public var currentSession: HermesSession?
|
||||
public var messageGroups: [MessageGroup] = []
|
||||
/// True from the moment the user sends a prompt until the ACP
|
||||
/// `promptComplete` event arrives. Covers the whole round-trip
|
||||
/// including auxiliary post-processing (title generation, usage
|
||||
/// accounting, etc.). UIs should prefer the `isGenerating` /
|
||||
/// `isPostProcessing` pair below — they distinguish "agent is
|
||||
/// thinking about your message" from "agent is closing out" and
|
||||
/// avoid the misleading "spinner after the reply has landed" UX
|
||||
/// we saw in pass-1 (M7 #4).
|
||||
public var isAgentWorking = false
|
||||
public var pendingPermission: PendingPermission?
|
||||
/// Mutated to trigger a scroll-to-bottom in the message list.
|
||||
public var scrollTrigger = UUID()
|
||||
|
||||
/// True while the assistant hasn't yet emitted a complete reply
|
||||
/// for the latest user prompt. Renders the prominent "Agent is
|
||||
/// thinking…" indicator in the chat. Flips false as soon as we've
|
||||
/// finalized an assistant message with content — even if the ACP
|
||||
/// `promptComplete` event hasn't arrived yet (Hermes auxiliary
|
||||
/// work like title generation delays that event).
|
||||
public var isGenerating: Bool {
|
||||
isAgentWorking && !isPostProcessing
|
||||
}
|
||||
|
||||
/// True while ACP hasn't closed out the prompt but the assistant
|
||||
/// has already finalized a reply the user can see. Renders a
|
||||
/// subtle "Finishing up…" pill instead of the prominent spinner.
|
||||
/// Avoids the pass-1 M7 #4 UX where users stared at "Agent is
|
||||
/// working…" forever because `promptComplete` was held up by
|
||||
/// auxiliary server-side work.
|
||||
public var isPostProcessing: Bool {
|
||||
guard isAgentWorking else { return false }
|
||||
guard let last = messages.last else { return false }
|
||||
return last.isAssistant && !last.content.isEmpty
|
||||
}
|
||||
|
||||
// Cumulative ACP token tracking (ACP returns tokens per prompt but DB has none)
|
||||
public private(set) var acpInputTokens = 0
|
||||
public private(set) var acpOutputTokens = 0
|
||||
|
||||
Reference in New Issue
Block a user