| 1 | export function guidanceNeedsRetry(state?: string): boolean { |
| 2 | return state === "uncertain" || state === "blocked"; |
| 3 | } |
| 4 | |
| 5 | export function guidanceIsInFlight(state?: string): boolean { |
| 6 | return state === "steer_accepted"; |
| 7 | } |
| 8 | |
| 9 | export function guidanceHasKnownPendingState(state?: string): boolean { |
| 10 | return state === undefined || state === "" || state === "queued" || state === "blocked" || state === "uncertain" || state === "steer_accepted"; |
| 11 | } |
| 12 | |
| 13 | export function guidanceIsEditable(item: { id?: string; state?: string; source?: string; paused?: boolean }): boolean { |
| 14 | if (!item.id || item.id.startsWith("local-")) return false; |
| 15 | if (item.paused || guidanceIsInFlight(item.state) || guidanceNeedsRetry(item.state)) return false; |
| 16 | return item.state === "queued" || item.state === undefined || item.state === ""; |
| 17 | } |
| 18 | |
| 19 | export function markGuidanceQueued<T extends { id: string; state?: string; paused?: boolean }>(items: T[], id: string): T[] { |
| 20 | return items.map((item) => item.id === id ? { ...item, state: "queued", paused: false } : item); |
| 21 | } |
| 22 | |
| 23 | // Resuming an already-unpaused inbox is an idempotent Controller drain kick. |
| 24 | export async function kickIdleGuidance( |
| 25 | setPaused: (tabId: string, paused: boolean) => Promise<void>, |
| 26 | tabId: string, |
| 27 | refresh: () => void, |
| 28 | ): Promise<void> { |
| 29 | await setPaused(tabId, false); |
| 30 | refresh(); |
| 31 | } |
| 32 | |
| 33 | // Exact equality avoids consuming a different queued item whose text merely |
| 34 | // contains the accepted steer (#6238). |
| 35 | export function guidanceTextMatches(queued: string, consumed: string): boolean { |
| 36 | const left = queued.trim(); |
| 37 | const right = consumed.trim(); |
| 38 | return Boolean(left && right && left === right); |
| 39 | } |
| 40 |