Day / week / month / all-time summary stats for a single device-tracker, by metric type. Multi-backend: pass `metric` to pick which upstream endpoint to hit.
metric='latency' → wraps POST /api/latency/stats (icmpingId).
Returns: dayAvgLatency, weekAvgLatency, monthAvgLatency, allTimeAvgLatency (ms); dayAvgLoss, weekAvgLoss, monthAvgLoss, allTimeAvgLoss (%); dayPingCount/weekPingCount/etc.; dayUptime/weekUptime/etc. (% successful pings); currentLatency, currentLoss, monitoringDuration (humanized).
metric='disk' → wraps POST /api/disk/stats (diskId).
Returns: dayGrowthKB, weekGrowthKB, monthGrowthKB, allTimeGrowthKB (negative = filling); estimatedFillTime (humanized projection from 7-day slope); plus current available/used measurements.
**This is a fixed-window summary, not time-buckets.** Comparing day vs month tells the LLM 'is this metric degrading?'. For the raw samples underneath it, call device_get({id, hours: N, include_logs: true}) — the log rows are opt-in there because they are the expensive half of that response.
Discovery: `target_id` is a TRACKER id, never a device id, and device_get is the only tool that hands one out. Both sit nested in its response and both survive its default summary shape — they are tracker identity, not log rows, so no flag is needed to see them:
latency → device.ping.icmping_id. `ping` is a single object, not a list: a device has at most one icmping tracker, and its key is icmping_id, not id.
disk → device.disks[].id, one entry per monitored volume (agent-collected or SNMP). Most devices carry none — an empty array means there is no disk tracker to summarize, not that the lookup failed.
Port-stats has no equivalent endpoint upstream and is omitted; if one lands later, add a third metric backend.
Permission: devices. Tag-scoped server-side via Devices::withUserTags() before stats are computed.
Examples:
device_metric_summary({metric: 'latency', target_id: 17})
device_metric_summary({metric: 'disk', target_id: 42})