( messages: Message[], submitCount: number, )
| 90 | const COOLDOWN_MS = 30 * 60 * 1000 |
| 91 | |
| 92 | export function useIssueFlagBanner( |
| 93 | messages: Message[], |
| 94 | submitCount: number, |
| 95 | ): boolean { |
| 96 | if (process.env.USER_TYPE !== 'ant') { |
| 97 | return false |
| 98 | } |
| 99 | |
| 100 | // biome-ignore lint/correctness/useHookAtTopLevel: process.env.USER_TYPE is a compile-time constant |
| 101 | const lastTriggeredAtRef = useRef(0) |
| 102 | // biome-ignore lint/correctness/useHookAtTopLevel: process.env.USER_TYPE is a compile-time constant |
| 103 | const activeForSubmitRef = useRef(-1) |
| 104 | |
| 105 | // Memoize the O(messages) scans. This hook runs on every REPL render |
| 106 | // (including every keystroke), but messages is stable during typing. |
| 107 | // isSessionContainerCompatible walks all messages + regex-tests each |
| 108 | // bash command — by far the heaviest work here. |
| 109 | // biome-ignore lint/correctness/useHookAtTopLevel: process.env.USER_TYPE is a compile-time constant |
| 110 | const shouldTrigger = useMemo( |
| 111 | () => isSessionContainerCompatible(messages) && hasFrictionSignal(messages), |
| 112 | [messages], |
| 113 | ) |
| 114 | |
| 115 | // Keep showing the banner until the user submits another message |
| 116 | if (activeForSubmitRef.current === submitCount) { |
| 117 | return true |
| 118 | } |
| 119 | |
| 120 | if (Date.now() - lastTriggeredAtRef.current < COOLDOWN_MS) { |
| 121 | return false |
| 122 | } |
| 123 | if (submitCount < MIN_SUBMIT_COUNT) { |
| 124 | return false |
| 125 | } |
| 126 | if (!shouldTrigger) { |
| 127 | return false |
| 128 | } |
| 129 | |
| 130 | lastTriggeredAtRef.current = Date.now() |
| 131 | activeForSubmitRef.current = submitCount |
| 132 | return true |
| 133 | } |
| 134 |
no test coverage detected