Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.synheart.ai/llms.txt

Use this file to discover all available pages before exploring further.

The behavior SDK does not compute these metrics in Dart/Kotlin/Swift directly. The canonical implementation runs inside the runtime’s behavior feature deriver — the SDK ships events and the runtime returns metric bundles. Apps see the results as fields on BehavioralMetrics inside BehaviorSessionSummary.

Window

A metric is computed either:
  • Over the entire session (default, returned in BehaviorSessionSummary.behavioralMetrics).
  • Over an arbitrary time range when you call calculateMetricsForTimeRange({startTimestampSeconds, endTimestampSeconds, sessionId?}).
When the window contains no events, every metric returns 0.0 and lists return empty.

The canonical 12

interactionIntensity

non_typing_interaction = total_events − interruption_events − typing_events
typing_equivalent      = total_typing_duration_sec / 10        // 10s typing = 1 "event"
total_interaction      = non_typing_interaction + typing_equivalent
events_per_minute      = total_interaction / session_duration_sec * 60
interaction_intensity  = clamp01(events_per_minute / 10)
interruption_events = notification_events + call_events + app_switch_events. Range [0, 1]. Anchored so ~10 events/min reads as full intensity.

taskSwitchRate

Exponential saturation:
task_switch_rate = clamp01( 1 − exp(−app_switches_per_min / 2.0) )
2 switches/min ≈ 0.63; high rate approaches 1.

taskSwitchCost

task_switch_cost_ms = clamp(0, 10_000, duration_ms / app_switch_count)   // 0 if no switches
task_switch_cost    = task_switch_cost_ms / 10_000                       // normalised 0..1
In the SDK, BehavioralMetrics.taskSwitchCost is the millisecond form (raw int); flux internally also exposes a normalised [0, 1] version.

idleTimeRatio

idle_time_ratio = clamp01( total_idle_time_sec / session_duration_sec )
Idle gaps are counted from the SDK’s idle-segment detector (gaps above the configured threshold).

activeTimeRatio

active_interaction_time_ms = duration_ms − total_idle_time_ms − task_switch_cost_ms
active_time_ratio          = clamp01( active_interaction_time_ms / duration_ms )
Note: this is not simply 1 − idle_time_ratio — task-switch cost is also subtracted.

notificationLoad

Exponential saturation:
notification_load = clamp01( 1 − exp(−notifications_per_min / 1.0) )
1 notification/min ≈ 0.63.

burstiness

Barabási burstiness over inter-event gaps:
B   = (σ − μ) / (σ + μ)         // σ = stddev, μ = mean of inter-event gaps
burstiness = clamp01( (B + 1) / 2 )
0.0 = perfectly regular (Poisson-like), 0.5 = random / no data, 1.0 = very bursty. Flux returns 0.5 when the session has no inter-event gaps yet.

behavioralDistractionScore

Weighted composite, fixed weights:
distraction_score = clamp01(
    0.35 * task_switch_rate
  + 0.30 * notification_load
  + 0.20 * fragmented_idle_ratio
  + 0.15 * scroll_jitter_rate
)
The four weights are constants in flux; they are not configurable per session.

focusHint

focus_hint = 1.0 − distraction_score
By construction, focusHint + distractionScore = 1.0. (Flux’s test test_focus_hint_is_inverse_of_distraction enforces this.)

fragmentedIdleRatio

fragmented_idle_ratio = max(0, idle_segment_count / session_duration_sec)
Note: the raw ratio can exceed 1.0 on very short sessions; flux clamps at the encoding step before HSI export.

scrollJitterRate

scroll_jitter_rate = clamp01( direction_reversals / (scroll_events − 1) )
                    if scroll_events > 1, else 0
A direction reversal counts when scrolling flips between up and down.

deepFocusBlocks

Engagement segments lasting at least DEEP_FOCUS_MIN_DURATION_SEC = 120 seconds (2 minutes), fixed in flux. Each block in the SDK shape:
class DeepFocusBlock {
  final String startAt;     // ISO 8601 timestamp
  final String endAt;       // ISO 8601 timestamp
  final int durationMs;     // duration in milliseconds
}
Flux internally returns just the count; the SDK enriches it with timing detail when constructing BehaviorSessionSummary.

Typing metrics (extension)

When a session contains typing events, the SDK adds TypingMetrics derived from typing-specific aggregations (formulas live in flux’s typing module):
FieldJSON keyRange / units
typingSpeedtyping_speedtaps/sec
typingCadenceStabilitytyping_cadence_stability0–1
typingCadenceVariabilitytyping_cadence_variabilityunitless (CV)
typingActivityRatiotyping_activity_ratio0–1
typingGapRatiotyping_gap_ratio0–1
typingBurstinesstyping_burstiness0–1
typingInteractionIntensitytyping_interaction_intensity0–1
clipboardActivityRateclipboard_activity_rate0–1
correctionRatecorrection_rate0–1
A few are defined as composite ratios (commented in behavior_gesture_detector.dart):
  • typing_interaction_intensity = w₁ · typing_speed_norm + w₂ · (1 − typing_gap_ratio) + w₃ · typing_cadence_stability (weights set in the runtime feature deriver).
  • clipboard_activity_rate = (copy + paste + cut) / (typing_taps + clipboard_actions).
  • correction_rate = (backspace + delete) / (typing_taps + backspace + delete).

NotificationSummary

FieldDefinition
notificationCountTotal notifications received during session.
notificationIgnoredNotifications with InterruptionAction.ignored.
notificationIgnoreRatenotificationIgnored / notificationCount (0 when no notifications).
notificationClusteringIndexVariance-based clustering measure (Fano-factor flavor) over inter-arrival times.
callCount, callIgnoredSame shape for incoming calls.

Numerical stability

Flux clamps:
  • All bounded outputs are wrapped in .clamp(0.0, 1.0) (or per-field bounds for raw mileage values like task_switch_cost_ms capped at 10000).
  • Empty input (no events, zero duration, no scrolls, etc.) returns the safe fallback (0.0 for ratios, 0.5 for burstiness).
If a metric ever surfaces outside its declared range, it’s a bug in flux — not a value to interpret.