# SafetyMonitorAgent — A2A Spec

**Product:** NomGap  
**Trigger:** Telemetry event `fasting.duration_milestone` where `hours IN [24, 42, 48, 72]`, or on-demand via `nomgap.safety.check(userId)`  
**Output:** Push notification (`refeeding_reminder` or `extended_fast_warning`) + structured safety brief logged as telemetry event

---

## Agent roster

| Step | Agent                      | Input                               | Output                                                              |
| ---- | -------------------------- | ----------------------------------- | ------------------------------------------------------------------- |
| 1    | `FastStateInspectorAgent`  | `userId`, current timestamp         | Active fasting session doc + elapsed hours + protocol name          |
| 2    | `ThresholdEvaluatorAgent`  | Fasting session + elapsed hours     | Safety level (`safe` / `caution` / `critical`) + recommended action |
| 3    | `SafetyNotificationAgent`  | Safety level + userId + protocol    | Push notification fired + telemetry event logged                    |
| 4    | `ExtendedFastHandoffAgent` | Session (hours ≥ 48) + safety brief | A2A handoff to `ExtendedFastDetectionAgent` (if escalation needed)  |

---

## Agent contracts

### FastStateInspectorAgent

```typescript
// Tool: nomgap.fasting.getSession (GET /fasting-sessions/active?userId=<id>)
input: {
  userId: string;
}
output: {
  sessionId: string;
  protocolId: string;
  protocolName: string;
  startedAt: string; // ISO 8601
  elapsedHours: number;
  targetHours: number;
  isActive: boolean;
}
```

### ThresholdEvaluatorAgent

```typescript
// Pure in-process evaluation — no external calls
// Thresholds defined in nomgap safety rules:
//   0-24h:  safe
//   24-42h: caution (electrolyte reminder at 24h)
//   42-48h: caution (pre-refeeding warning)
//   48h+:   critical (refeeding_reminder + medical supervision flag)
//   72h+:   critical (escalate to ExtendedFastDetectionAgent)
input: {
  sessionId: string;
  elapsedHours: number;
  protocolName: string;
  targetHours: number;
}
output: {
  safetyLevel: 'safe' | 'caution' | 'critical';
  pushType: 'refeeding_reminder' | 'extended_fast_warning' | 'electrolyte_reminder' | null;
  escalate: boolean; // true when elapsedHours >= 72
  messageVariables: Record<string, string>; // { hours, protocolName, refeedingTip }
}
```

### SafetyNotificationAgent

```typescript
// Tool: nomgap.push.fire (POST platform-service /push-triggers)
// Tool: platform.telemetry.events (POST platform-service /telemetry/events)
input: {
  userId: string;
  pushType: string;
  messageVariables: Record<string, string>;
  sessionId: string;
  safetyLevel: string;
}
output: {
  notified: boolean;
  pushTriggerId: string;
  telemetryEventId: string;
}
```

### ExtendedFastHandoffAgent

```typescript
// Fires only when ThresholdEvaluatorAgent.escalate === true (hours >= 72)
// A2A handoff artifact is emitted as a telemetry event so any subscribed
// ExtendedFastDetectionAgent can pick it up.
input: {
  userId: string;
  sessionId: string;
  elapsedHours: number;
  safetyLevel: 'critical';
}
output: {
  handoffEventId: string;
  handoffPayload: {
    productId: 'nomgap';
    agentTarget: 'ExtendedFastDetectionAgent';
    userId: string;
    sessionId: string;
    elapsedHours: number;
    triggeredAt: string;
  }
}
```

---

## Data model

```typescript
interface SafetyCheckResult {
  userId: string;
  productId: 'nomgap';
  sessionId: string;
  elapsedHours: number;
  safetyLevel: 'safe' | 'caution' | 'critical';
  actionsTaken: string[]; // e.g. ['push:refeeding_reminder', 'telemetry:logged', 'handoff:ExtendedFastDetectionAgent']
  checkedAt: string; // ISO 8601
}
```

---

## Threshold table

| Elapsed hours | Safety level | Push type               | Escalate? |
| ------------- | ------------ | ----------------------- | --------- |
| < 24 h        | `safe`       | none                    | no        |
| 24 h          | `caution`    | `electrolyte_reminder`  | no        |
| 42 h          | `caution`    | `refeeding_reminder`    | no        |
| 48 h          | `critical`   | `refeeding_reminder`    | no        |
| 72 h+         | `critical`   | `extended_fast_warning` | **yes**   |

---

## Error handling

- If the user has no active fasting session, `FastStateInspectorAgent` returns `isActive: false` and the pipeline exits with no action.
- If `nomgap.push.fire` fails, the telemetry event is still logged (best-effort notification).
- If `ExtendedFastHandoffAgent` cannot emit the handoff event, the critical push notification still fires — escalation failure is non-blocking.
- Idempotent within a 1-hour window: a safety check that already fired the same `pushType` within the last 60 minutes is skipped.

---

## MCP tool surface

```typescript
nomgap.safety.check(userId: string): SafetyCheckResult
nomgap.safety.getThresholds(): ThresholdTable   // returns the threshold table above
```

---

## Implementation checklist

- [ ] `FastStateInspectorAgent` — thin wrapper over `GET /fasting-sessions/active`
- [ ] `ThresholdEvaluatorAgent` — pure evaluation logic (no I/O), unit-testable
- [ ] `SafetyNotificationAgent` — calls `nomgap.push.fire` + logs telemetry event
- [ ] `ExtendedFastHandoffAgent` — emits A2A handoff telemetry event for ≥ 72 h fasts
- [ ] Cron or event subscription: subscribe to `fasting.duration_milestone` telemetry events
- [ ] Deduplication guard: skip if same `(userId, pushType)` fired within 60 minutes
- [ ] MCP tool registration: `nomgap.safety.check` + `nomgap.safety.getThresholds`
- [ ] Unit tests for `ThresholdEvaluatorAgent` covering all 5 threshold rows
