()
| 199 | * Called separately from the heartbeat job (e.g., weekly check). |
| 200 | */ |
| 201 | export async function notifyExtendedOutages(): Promise<{ notified: number }> { |
| 202 | let notified = 0; |
| 203 | |
| 204 | try { |
| 205 | // Find production agents failing for 7+ days |
| 206 | const result = await query( |
| 207 | `SELECT s.agent_url, s.headline, s.status, s.status_changed_at |
| 208 | FROM agent_compliance_status s |
| 209 | LEFT JOIN agent_registry_metadata m ON m.agent_url = s.agent_url |
| 210 | WHERE s.status IN ('failing', 'degraded') |
| 211 | AND s.status_changed_at < NOW() - INTERVAL '7 days' |
| 212 | AND COALESCE(m.lifecycle_stage, 'production') = 'production' |
| 213 | AND COALESCE(m.compliance_opt_out, FALSE) = FALSE`, |
| 214 | ); |
| 215 | |
| 216 | for (const row of result.rows) { |
| 217 | const userIds = await resolveAgentOwnerUserIds(row.agent_url); |
| 218 | const name = agentDisplayName(row.agent_url); |
| 219 | const days = Math.floor((Date.now() - new Date(row.status_changed_at).getTime()) / (1000 * 60 * 60 * 24)); |
| 220 | |
| 221 | for (const userId of userIds) { |
| 222 | try { |
| 223 | const alreadySent = await notificationDb.exists(userId, 'compliance_extended_outage', row.agent_url); |
| 224 | if (alreadySent) continue; |
| 225 | |
| 226 | await notifyUser({ |
| 227 | recipientUserId: userId, |
| 228 | type: 'compliance_extended_outage', |
| 229 | referenceId: row.agent_url, |
| 230 | referenceType: 'agent', |
| 231 | title: `Your agent ${name} has been ${row.status} for ${days} days. Buyers are seeing failures.`, |
| 232 | url: '/registry?tab=agents', |
| 233 | }); |
| 234 | notified++; |
| 235 | } catch (error) { |
| 236 | logger.error({ error, userId, agentUrl: row.agent_url }, 'Failed to send extended outage notification'); |
| 237 | } |
| 238 | } |
| 239 | } |
| 240 | } catch (error) { |
| 241 | logger.error({ error }, 'Failed to check for extended outages'); |
| 242 | } |
| 243 | |
| 244 | return { notified }; |
| 245 | } |
nothing calls this directly
no test coverage detected