@elizaos/plugin-health 2.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +107 -0
- package/dist/actions/index.d.ts +20 -0
- package/dist/actions/index.d.ts.map +1 -0
- package/dist/actions/index.js +5 -0
- package/dist/actions/index.js.map +1 -0
- package/dist/anchors/index.d.ts +19 -0
- package/dist/anchors/index.d.ts.map +1 -0
- package/dist/anchors/index.js +9 -0
- package/dist/anchors/index.js.map +1 -0
- package/dist/connectors/contract-stubs.d.ts +112 -0
- package/dist/connectors/contract-stubs.d.ts.map +1 -0
- package/dist/connectors/contract-stubs.js +1 -0
- package/dist/connectors/contract-stubs.js.map +1 -0
- package/dist/connectors/index.d.ts +28 -0
- package/dist/connectors/index.d.ts.map +1 -0
- package/dist/connectors/index.js +202 -0
- package/dist/connectors/index.js.map +1 -0
- package/dist/contracts/circadian-default.d.ts +15 -0
- package/dist/contracts/circadian-default.d.ts.map +1 -0
- package/dist/contracts/circadian-default.js +30 -0
- package/dist/contracts/circadian-default.js.map +1 -0
- package/dist/contracts/circadian.d.ts +92 -0
- package/dist/contracts/circadian.d.ts.map +1 -0
- package/dist/contracts/circadian.js +14 -0
- package/dist/contracts/circadian.js.map +1 -0
- package/dist/contracts/health.d.ts +9 -0
- package/dist/contracts/health.d.ts.map +1 -0
- package/dist/contracts/health.js +21 -0
- package/dist/contracts/health.js.map +1 -0
- package/dist/contracts/lifeops-connector-degradation.d.ts +9 -0
- package/dist/contracts/lifeops-connector-degradation.d.ts.map +1 -0
- package/dist/contracts/lifeops-connector-degradation.js +17 -0
- package/dist/contracts/lifeops-connector-degradation.js.map +1 -0
- package/dist/contracts/lifeops.d.ts +3123 -0
- package/dist/contracts/lifeops.d.ts.map +1 -0
- package/dist/contracts/lifeops.js +635 -0
- package/dist/contracts/lifeops.js.map +1 -0
- package/dist/contracts/permissions.d.ts +39 -0
- package/dist/contracts/permissions.d.ts.map +1 -0
- package/dist/contracts/permissions.js +1 -0
- package/dist/contracts/permissions.js.map +1 -0
- package/dist/default-packs/bedtime.d.ts +14 -0
- package/dist/default-packs/bedtime.d.ts.map +1 -0
- package/dist/default-packs/bedtime.js +48 -0
- package/dist/default-packs/bedtime.js.map +1 -0
- package/dist/default-packs/contract-stubs.d.ts +161 -0
- package/dist/default-packs/contract-stubs.d.ts.map +1 -0
- package/dist/default-packs/contract-stubs.js +1 -0
- package/dist/default-packs/contract-stubs.js.map +1 -0
- package/dist/default-packs/index.d.ts +18 -0
- package/dist/default-packs/index.d.ts.map +1 -0
- package/dist/default-packs/index.js +39 -0
- package/dist/default-packs/index.js.map +1 -0
- package/dist/default-packs/sleep-recap.d.ts +14 -0
- package/dist/default-packs/sleep-recap.d.ts.map +1 -0
- package/dist/default-packs/sleep-recap.js +51 -0
- package/dist/default-packs/sleep-recap.js.map +1 -0
- package/dist/default-packs/wake-up.d.ts +14 -0
- package/dist/default-packs/wake-up.d.ts.map +1 -0
- package/dist/default-packs/wake-up.js +61 -0
- package/dist/default-packs/wake-up.js.map +1 -0
- package/dist/health-bridge/health-bridge.d.ts +57 -0
- package/dist/health-bridge/health-bridge.d.ts.map +1 -0
- package/dist/health-bridge/health-bridge.js +558 -0
- package/dist/health-bridge/health-bridge.js.map +1 -0
- package/dist/health-bridge/health-connectors.d.ts +23 -0
- package/dist/health-bridge/health-connectors.d.ts.map +1 -0
- package/dist/health-bridge/health-connectors.js +1018 -0
- package/dist/health-bridge/health-connectors.js.map +1 -0
- package/dist/health-bridge/health-oauth.d.ts +62 -0
- package/dist/health-bridge/health-oauth.d.ts.map +1 -0
- package/dist/health-bridge/health-oauth.js +432 -0
- package/dist/health-bridge/health-oauth.js.map +1 -0
- package/dist/health-bridge/health-provider-registry.d.ts +89 -0
- package/dist/health-bridge/health-provider-registry.d.ts.map +1 -0
- package/dist/health-bridge/health-provider-registry.js +141 -0
- package/dist/health-bridge/health-provider-registry.js.map +1 -0
- package/dist/health-bridge/health-records.d.ts +14 -0
- package/dist/health-bridge/health-records.d.ts.map +1 -0
- package/dist/health-bridge/health-records.js +45 -0
- package/dist/health-bridge/health-records.js.map +1 -0
- package/dist/health-bridge/index.d.ts +22 -0
- package/dist/health-bridge/index.d.ts.map +1 -0
- package/dist/health-bridge/index.js +7 -0
- package/dist/health-bridge/index.js.map +1 -0
- package/dist/health-bridge/service-normalize-health.d.ts +3 -0
- package/dist/health-bridge/service-normalize-health.d.ts.map +1 -0
- package/dist/health-bridge/service-normalize-health.js +96 -0
- package/dist/health-bridge/service-normalize-health.js.map +1 -0
- package/dist/index.d.ts +41 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +62 -0
- package/dist/index.js.map +1 -0
- package/dist/screen-time/index.d.ts +23 -0
- package/dist/screen-time/index.d.ts.map +1 -0
- package/dist/screen-time/index.js +1 -0
- package/dist/screen-time/index.js.map +1 -0
- package/dist/sleep/awake-probability.d.ts +11 -0
- package/dist/sleep/awake-probability.d.ts.map +1 -0
- package/dist/sleep/awake-probability.js +163 -0
- package/dist/sleep/awake-probability.js.map +1 -0
- package/dist/sleep/circadian-rules.d.ts +45 -0
- package/dist/sleep/circadian-rules.d.ts.map +1 -0
- package/dist/sleep/circadian-rules.js +258 -0
- package/dist/sleep/circadian-rules.js.map +1 -0
- package/dist/sleep/index.d.ts +21 -0
- package/dist/sleep/index.d.ts.map +1 -0
- package/dist/sleep/index.js +11 -0
- package/dist/sleep/index.js.map +1 -0
- package/dist/sleep/sleep-cycle-dispatch.d.ts +75 -0
- package/dist/sleep/sleep-cycle-dispatch.d.ts.map +1 -0
- package/dist/sleep/sleep-cycle-dispatch.js +102 -0
- package/dist/sleep/sleep-cycle-dispatch.js.map +1 -0
- package/dist/sleep/sleep-cycle.d.ts +38 -0
- package/dist/sleep/sleep-cycle.d.ts.map +1 -0
- package/dist/sleep/sleep-cycle.js +418 -0
- package/dist/sleep/sleep-cycle.js.map +1 -0
- package/dist/sleep/sleep-episode-store.d.ts +25 -0
- package/dist/sleep/sleep-episode-store.d.ts.map +1 -0
- package/dist/sleep/sleep-episode-store.js +69 -0
- package/dist/sleep/sleep-episode-store.js.map +1 -0
- package/dist/sleep/sleep-episode-types.d.ts +38 -0
- package/dist/sleep/sleep-episode-types.d.ts.map +1 -0
- package/dist/sleep/sleep-episode-types.js +14 -0
- package/dist/sleep/sleep-episode-types.js.map +1 -0
- package/dist/sleep/sleep-recap.d.ts +19 -0
- package/dist/sleep/sleep-recap.d.ts.map +1 -0
- package/dist/sleep/sleep-recap.js +1 -0
- package/dist/sleep/sleep-recap.js.map +1 -0
- package/dist/sleep/sleep-regularity.d.ts +19 -0
- package/dist/sleep/sleep-regularity.d.ts.map +1 -0
- package/dist/sleep/sleep-regularity.js +242 -0
- package/dist/sleep/sleep-regularity.js.map +1 -0
- package/dist/sleep/sleep-wake-events.d.ts +58 -0
- package/dist/sleep/sleep-wake-events.d.ts.map +1 -0
- package/dist/sleep/sleep-wake-events.js +135 -0
- package/dist/sleep/sleep-wake-events.js.map +1 -0
- package/dist/sleep/source-reliability.d.ts +38 -0
- package/dist/sleep/source-reliability.d.ts.map +1 -0
- package/dist/sleep/source-reliability.js +62 -0
- package/dist/sleep/source-reliability.js.map +1 -0
- package/dist/util/index.d.ts +10 -0
- package/dist/util/index.d.ts.map +1 -0
- package/dist/util/index.js +3 -0
- package/dist/util/index.js.map +1 -0
- package/dist/util/normalize.d.ts +22 -0
- package/dist/util/normalize.d.ts.map +1 -0
- package/dist/util/normalize.js +62 -0
- package/dist/util/normalize.js.map +1 -0
- package/dist/util/time-util.d.ts +10 -0
- package/dist/util/time-util.d.ts.map +1 -0
- package/dist/util/time-util.js +14 -0
- package/dist/util/time-util.js.map +1 -0
- package/dist/util/time.d.ts +17 -0
- package/dist/util/time.d.ts.map +1 -0
- package/dist/util/time.js +152 -0
- package/dist/util/time.js.map +1 -0
- package/dist/util/token-encryption.d.ts +42 -0
- package/dist/util/token-encryption.d.ts.map +1 -0
- package/dist/util/token-encryption.js +96 -0
- package/dist/util/token-encryption.js.map +1 -0
- package/package.json +46 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/sleep/circadian-rules.ts"],"sourcesContent":["/**\n * Named-rules evidence scorer for the circadian state machine.\n *\n * Rules are declared as a flat table so each rule is trivial to audit and\n * easy to extend. Every rule is a pure predicate over the scorer inputs\n * that may return one `CircadianRuleFiring` (or `null` when the rule\n * doesn't apply this tick). The runner accumulates firings into a per-state\n * totals map that the state machine layer then ranks.\n *\n * Weights, thresholds, and stability windows are the canonical ones from\n * `docs/sleep-wake-spec.md` §3. Anything tunable lives at the top of this\n * file so spec and code never drift.\n */\n\nimport type {\n LifeOpsActivitySignal,\n LifeOpsCircadianRuleFiring,\n LifeOpsCircadianState,\n LifeOpsPersonalBaseline,\n LifeOpsRegularityClass,\n} from \"../contracts/health.js\";\nimport { getZonedDateParts } from \"../util/time.js\";\nimport { parseIsoMs } from \"../util/time-util.js\";\nimport type { LifeOpsActivityWindow } from \"./sleep-cycle.js\";\n\nexport const MIN_STABILITY_WINDOW_MS = 5 * 60_000;\nexport const WAKE_CONFIRM_WINDOW_MS = 10 * 60_000;\nexport const SLEEP_ONSET_WINDOW_MS = 20 * 60_000;\nconst AWAKE_EVIDENCE_MAX_AGE_MS = 20 * 60_000;\nconst NAP_MAX_DURATION_MS = 4 * 60 * 60_000;\n\nexport type CircadianRuleFiring = LifeOpsCircadianRuleFiring;\n\nexport interface CircadianScorerResult {\n firings: CircadianRuleFiring[];\n totals: Record<LifeOpsCircadianState, number>;\n}\n\ninterface ScorerInputs {\n nowMs: number;\n timezone: string;\n signals: readonly LifeOpsActivitySignal[];\n windows: readonly LifeOpsActivityWindow[];\n baseline: LifeOpsPersonalBaseline | null;\n regularityClass: LifeOpsRegularityClass;\n hasCurrentSleepEpisode: boolean;\n currentSleepStartedAtMs: number | null;\n lastSleepEndedAtMs: number | null;\n currentEpisodeLikelyNap: boolean;\n}\n\nfunction localHour(nowMs: number, timezone: string): number {\n const parts = getZonedDateParts(new Date(nowMs), timezone);\n return parts.hour + parts.minute / 60;\n}\n\nfunction isOvernight(nowMs: number, timezone: string): boolean {\n const hour = localHour(nowMs, timezone);\n return hour >= 22 || hour < 6;\n}\n\nfunction signalAge(\n signal: LifeOpsActivitySignal,\n nowMs: number,\n): number | null {\n const observedAt = parseIsoMs(signal.observedAt);\n return observedAt === null ? null : nowMs - observedAt;\n}\n\ntype Rule = (inputs: ScorerInputs) => CircadianRuleFiring | null;\n\nfunction findSignal(\n inputs: ScorerInputs,\n predicate: (signal: LifeOpsActivitySignal) => boolean,\n): { signal: LifeOpsActivitySignal; ageMs: number } | null {\n for (const signal of inputs.signals) {\n if (!predicate(signal)) continue;\n const ageMs = signalAge(signal, inputs.nowMs);\n if (ageMs === null) continue;\n return { signal, ageMs };\n }\n return null;\n}\n\n/**\n * The canonical rule set. Ordered by runtime cost (cheap predicates first).\n * Each rule is a pure function with no shared state — tests can exercise\n * individual rules by calling them directly.\n */\nconst RULES: readonly Rule[] = [\n // manual.override — user attestation, 4h TTL.\n function manualOverride(inputs) {\n const hit = findSignal(\n inputs,\n (s) =>\n s.platform === \"manual_override\" && s.metadata.userAttested === true,\n );\n if (!hit || hit.ageMs > 4 * 60 * 60_000) return null;\n const kind = String(hit.signal.metadata.manualOverrideKind ?? \"\");\n return {\n name: \"manual.override\",\n contributes: kind === \"going_to_bed\" ? \"sleeping\" : \"awake\",\n weight: 1.0,\n observedAt: hit.signal.observedAt,\n reason: `user attested ${kind}`,\n };\n },\n\n // healthkit.isSleepingNow — any fresh sleep sample.\n function healthkitSleep(inputs) {\n const hit = findSignal(\n inputs,\n (s) =>\n s.source === \"mobile_health\" && s.health?.sleep.isSleeping === true,\n );\n if (!hit || hit.ageMs > 2 * 60 * 60_000) return null;\n return {\n name: \"healthkit.isSleepingNow\",\n contributes: \"sleeping\",\n weight: 0.95,\n observedAt: hit.signal.observedAt,\n reason: \"HealthKit reports isSleeping=true\",\n };\n },\n\n // hid.idleGt20m — HID idle past the awake-evidence timeout.\n function hidIdle(inputs) {\n const hit = findSignal(\n inputs,\n (s) =>\n s.source === \"desktop_interaction\" &&\n typeof s.idleTimeSeconds === \"number\" &&\n s.idleTimeSeconds >= 20 * 60,\n );\n if (!hit || hit.ageMs > AWAKE_EVIDENCE_MAX_AGE_MS) return null;\n return {\n name: \"hid.idleGt20m\",\n contributes: isOvernight(inputs.nowMs, inputs.timezone)\n ? \"sleeping\"\n : \"winding_down\",\n weight: 0.8,\n observedAt: hit.signal.observedAt,\n reason: `HID idle >=20 min (${hit.signal.idleTimeSeconds}s)`,\n };\n },\n\n // desktop.lockedGt30m — session lock sustained past 30 min.\n function desktopLocked(inputs) {\n const hit = findSignal(\n inputs,\n (s) => s.source === \"desktop_power\" && s.state === \"locked\",\n );\n if (!hit || hit.ageMs < 30 * 60_000) return null;\n return {\n name: \"desktop.lockedGt30m\",\n contributes: isOvernight(inputs.nowMs, inputs.timezone)\n ? \"sleeping\"\n : \"winding_down\",\n weight: 0.85,\n observedAt: hit.signal.observedAt,\n reason: \"session locked >=30 min\",\n };\n },\n\n // desktop.wakeNotification — recent system wake event.\n function desktopWake(inputs) {\n const hit = findSignal(\n inputs,\n (s) =>\n s.source === \"desktop_power\" &&\n (s.state === \"active\" ||\n s.metadata.event === \"didWake\" ||\n s.metadata.event === \"screensDidWake\"),\n );\n if (!hit || hit.ageMs > WAKE_CONFIRM_WINDOW_MS) return null;\n return {\n name: \"desktop.wakeNotification\",\n contributes: \"waking\",\n weight: 0.92,\n observedAt: hit.signal.observedAt,\n reason: \"recent NSWorkspace wake notification\",\n };\n },\n\n // message.outboundRecent — outbound owner message in the last 10 minutes.\n function messageOutbound(inputs) {\n const hit = findSignal(\n inputs,\n (s) =>\n s.source === \"imessage_outbound\" ||\n (s.source === \"connector_activity\" &&\n (s.metadata.eventType === \"MESSAGE_RECEIVED\" ||\n s.metadata.direction === \"outbound_by_owner\")),\n );\n if (!hit || hit.ageMs > 10 * 60_000) return null;\n return {\n name: \"message.outboundRecent\",\n contributes: \"awake\",\n weight: 0.88,\n observedAt: hit.signal.observedAt,\n reason: \"outbound message within 10 min\",\n };\n },\n\n // continuity.iphoneDisconnected — paired iPhone absent overnight.\n function continuityIPhone(inputs) {\n if (!isOvernight(inputs.nowMs, inputs.timezone)) return null;\n const hit = findSignal(\n inputs,\n (s) =>\n s.source === \"mobile_device\" &&\n typeof s.platform === \"string\" &&\n s.platform.startsWith(\"macos_continuity\") &&\n s.state !== \"active\",\n );\n if (!hit || hit.ageMs > 60 * 60_000) return null;\n return {\n name: \"continuity.iphoneDisconnected\",\n contributes: \"sleeping\",\n weight: 0.5,\n observedAt: hit.signal.observedAt,\n reason: \"paired iPhone disconnected overnight\",\n };\n },\n\n // gap.noSignalsGt2hOvernight — no activity windows for 2h+ at night.\n function activityGap(inputs) {\n const latestWindow = inputs.windows[inputs.windows.length - 1];\n if (!latestWindow) return null;\n const gapMs = inputs.nowMs - latestWindow.endMs;\n if (gapMs < 2 * 60 * 60_000) return null;\n const hour = localHour(inputs.nowMs, inputs.timezone);\n if (!(hour >= 22 || hour < 10)) return null;\n return {\n name: \"gap.noSignalsGt2hOvernight\",\n contributes: \"sleeping\",\n weight: Math.min(0.9, 0.3 + gapMs / (8 * 60 * 60_000)),\n observedAt: new Date(latestWindow.endMs).toISOString(),\n reason: `no activity for ${Math.round(gapMs / 60_000)} min overnight`,\n };\n },\n\n // baseline.currentHourLikely[Asleep|Awake] — personal bedtime prior.\n function baselinePrior(inputs) {\n if (!inputs.baseline) return null;\n if (\n inputs.regularityClass !== \"regular\" &&\n inputs.regularityClass !== \"very_regular\"\n ) {\n return null;\n }\n const hour = localHour(inputs.nowMs, inputs.timezone);\n const { medianBedtimeLocalHour: bedtime, medianWakeLocalHour: wake } =\n inputs.baseline;\n const normalized = hour < 12 ? hour + 24 : hour;\n if (normalized >= bedtime || normalized < wake + 12) {\n return {\n name: \"baseline.currentHourLikelyAsleep\",\n contributes: \"sleeping\",\n weight: 0.35,\n observedAt: new Date(inputs.nowMs).toISOString(),\n reason: `within baseline bedtime window (${bedtime.toFixed(1)}h-${wake.toFixed(1)}h)`,\n };\n }\n if (normalized >= wake && normalized < wake + 4) {\n return {\n name: \"baseline.currentHourLikelyAwake\",\n contributes: \"awake\",\n weight: 0.3,\n observedAt: new Date(inputs.nowMs).toISOString(),\n reason: \"within baseline morning window\",\n };\n }\n return null;\n },\n\n // active.signalRecent — generic active presence within 5 min.\n function activeSignalRecent(inputs) {\n const latest = [...inputs.signals]\n .map((signal) => {\n const ageMs = signalAge(signal, inputs.nowMs);\n return ageMs === null ? null : { signal, ageMs };\n })\n .filter(\n (\n candidate,\n ): candidate is { signal: LifeOpsActivitySignal; ageMs: number } =>\n candidate !== null,\n )\n .sort((left, right) => left.ageMs - right.ageMs)[0];\n if (!latest) return null;\n if (latest.signal.state !== \"active\" || latest.ageMs > 5 * 60_000) {\n return null;\n }\n return {\n name: \"active.signalRecent\",\n contributes: \"awake\",\n weight: 0.7,\n observedAt: latest.signal.observedAt,\n reason: \"active signal within 5 min\",\n };\n },\n\n // episode.(sleep|nap)InProgress — current sleep episode.\n function currentEpisode(inputs) {\n if (\n !inputs.hasCurrentSleepEpisode ||\n inputs.currentSleepStartedAtMs === null\n ) {\n return null;\n }\n const duration = inputs.nowMs - inputs.currentSleepStartedAtMs;\n const isNap =\n inputs.currentEpisodeLikelyNap && duration < NAP_MAX_DURATION_MS;\n return {\n name: isNap ? \"episode.napInProgress\" : \"episode.sleepInProgress\",\n contributes: isNap ? \"napping\" : \"sleeping\",\n weight: 0.85,\n observedAt: new Date(inputs.currentSleepStartedAtMs).toISOString(),\n reason: isNap ? \"nap episode in progress\" : \"sleep episode in progress\",\n };\n },\n\n // episode.justWoke — wake anchor inside the confirm window.\n function justWoke(inputs) {\n if (inputs.lastSleepEndedAtMs === null) return null;\n const age = inputs.nowMs - inputs.lastSleepEndedAtMs;\n if (age < 0 || age > WAKE_CONFIRM_WINDOW_MS) return null;\n return {\n name: \"episode.justWoke\",\n contributes: \"waking\",\n weight: 0.7,\n observedAt: new Date(inputs.lastSleepEndedAtMs).toISOString(),\n reason: \"wake anchor within stability window\",\n };\n },\n];\n\nfunction emptyTotals(): Record<LifeOpsCircadianState, number> {\n return {\n awake: 0,\n winding_down: 0,\n sleeping: 0,\n waking: 0,\n napping: 0,\n unclear: 0,\n };\n}\n\n/**\n * Evaluate every rule in the table and aggregate firings by state.\n *\n * Pure; no I/O. The state machine layer is responsible for picking the\n * top bucket, applying stability-window hysteresis, and translating the\n * result into a calibrated confidence.\n */\nexport function scoreCircadianRules(\n inputs: ScorerInputs,\n): CircadianScorerResult {\n const firings: CircadianRuleFiring[] = [];\n const totals = emptyTotals();\n for (const rule of RULES) {\n const firing = rule(inputs);\n if (!firing) continue;\n firings.push(firing);\n totals[firing.contributes] += firing.weight;\n }\n return { firings, totals };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sleep / circadian / awake-probability domain entry point.
|
|
3
|
+
*
|
|
4
|
+
* The implementation files in this directory were moved from
|
|
5
|
+
* `eliza/plugins/app-lifeops/src/lifeops/` in Wave-1 (W1-B). They are pure
|
|
6
|
+
* domain helpers — no LifeOpsService coupling, no SQL repository coupling
|
|
7
|
+
* (the small set of repository methods sleep-episode-store needs is captured
|
|
8
|
+
* by the structural `SleepEpisodeRepository` interface in
|
|
9
|
+
* `sleep-episode-types.ts`).
|
|
10
|
+
*/
|
|
11
|
+
export * from "./awake-probability.js";
|
|
12
|
+
export * from "./circadian-rules.js";
|
|
13
|
+
export * from "./sleep-cycle.js";
|
|
14
|
+
export * from "./sleep-cycle-dispatch.js";
|
|
15
|
+
export * from "./sleep-episode-store.js";
|
|
16
|
+
export * from "./sleep-episode-types.js";
|
|
17
|
+
export * from "./sleep-recap.js";
|
|
18
|
+
export * from "./sleep-regularity.js";
|
|
19
|
+
export * from "./sleep-wake-events.js";
|
|
20
|
+
export * from "./source-reliability.js";
|
|
21
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sleep/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,cAAc,wBAAwB,CAAC;AACvC,cAAc,sBAAsB,CAAC;AACrC,cAAc,kBAAkB,CAAC;AACjC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,0BAA0B,CAAC;AACzC,cAAc,0BAA0B,CAAC;AACzC,cAAc,kBAAkB,CAAC;AACjC,cAAc,uBAAuB,CAAC;AACtC,cAAc,wBAAwB,CAAC;AACvC,cAAc,yBAAyB,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export * from "./awake-probability.js";
|
|
2
|
+
export * from "./circadian-rules.js";
|
|
3
|
+
export * from "./sleep-cycle.js";
|
|
4
|
+
export * from "./sleep-cycle-dispatch.js";
|
|
5
|
+
export * from "./sleep-episode-store.js";
|
|
6
|
+
export * from "./sleep-episode-types.js";
|
|
7
|
+
export * from "./sleep-recap.js";
|
|
8
|
+
export * from "./sleep-regularity.js";
|
|
9
|
+
export * from "./sleep-wake-events.js";
|
|
10
|
+
export * from "./source-reliability.js";
|
|
11
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/sleep/index.ts"],"sourcesContent":["/**\n * Sleep / circadian / awake-probability domain entry point.\n *\n * The implementation files in this directory were moved from\n * `eliza/plugins/app-lifeops/src/lifeops/` in Wave-1 (W1-B). They are pure\n * domain helpers — no LifeOpsService coupling, no SQL repository coupling\n * (the small set of repository methods sleep-episode-store needs is captured\n * by the structural `SleepEpisodeRepository` interface in\n * `sleep-episode-types.ts`).\n */\n\nexport * from \"./awake-probability.js\";\nexport * from \"./circadian-rules.js\";\nexport * from \"./sleep-cycle.js\";\nexport * from \"./sleep-cycle-dispatch.js\";\nexport * from \"./sleep-episode-store.js\";\nexport * from \"./sleep-episode-types.js\";\nexport * from \"./sleep-recap.js\";\nexport * from \"./sleep-regularity.js\";\nexport * from \"./sleep-wake-events.js\";\nexport * from \"./source-reliability.js\";\n"],"mappings":"AAWA,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;","names":[]}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { LifeOpsCircadianState, LifeOpsPersonalBaseline, LifeOpsRegularityClass, LifeOpsScheduleRegularity } from "../contracts/health.js";
|
|
2
|
+
import type { SleepRecap } from "./sleep-recap.js";
|
|
3
|
+
export declare const MORNING_CHECKIN_WINDOW_MINUTES: number;
|
|
4
|
+
export declare const NIGHT_CHECKIN_LEAD_MINUTES: number;
|
|
5
|
+
export declare const DEFAULT_IRREGULAR_BEDTIME_LOCAL = "23:00";
|
|
6
|
+
export interface CheckinSleepCycleState {
|
|
7
|
+
readonly circadianState: LifeOpsCircadianState;
|
|
8
|
+
readonly wakeAt: string | null;
|
|
9
|
+
readonly timezone?: string;
|
|
10
|
+
readonly regularity?: {
|
|
11
|
+
readonly regularityClass: LifeOpsRegularityClass;
|
|
12
|
+
};
|
|
13
|
+
readonly relativeTime: {
|
|
14
|
+
readonly minutesUntilBedtimeTarget: number | null;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* For owners whose schedule is `irregular` / `very_irregular`, the relative
|
|
19
|
+
* time resolver leaves `bedtimeTargetAt` null because no projection is
|
|
20
|
+
* trustworthy. Without a fallback the night summary never fires for these
|
|
21
|
+
* users. Compute "minutes until the next occurrence of `localBedtime`
|
|
22
|
+
* (local-time HH:MM) in `timezone`" — today if still upcoming, otherwise
|
|
23
|
+
* tomorrow.
|
|
24
|
+
*
|
|
25
|
+
* Returns null when inputs are missing or invalid; callers fall back to the
|
|
26
|
+
* normal bedtime-projection path in that case.
|
|
27
|
+
*/
|
|
28
|
+
export declare function minutesUntilLocalBedtime(args: {
|
|
29
|
+
readonly now: Date;
|
|
30
|
+
readonly timezone: string;
|
|
31
|
+
readonly localBedtime: string;
|
|
32
|
+
}): number | null;
|
|
33
|
+
export declare function shouldRunMorningCheckinFromSleepCycle(args: {
|
|
34
|
+
readonly state: CheckinSleepCycleState | null;
|
|
35
|
+
readonly now: Date;
|
|
36
|
+
}): boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Decide whether the night summary should fire on this scheduler tick.
|
|
39
|
+
*
|
|
40
|
+
* Triggers (any one is sufficient):
|
|
41
|
+
* 1. `circadianState === "winding_down"` (early signal from HID-idle / lock).
|
|
42
|
+
* 2. `circadianState` is `awake` or `waking` AND
|
|
43
|
+
* `minutesUntilBedtimeTarget` is inside `NIGHT_CHECKIN_LEAD_MINUTES`.
|
|
44
|
+
* 3. Irregular-owner fallback: `regularityClass` is `irregular` /
|
|
45
|
+
* `very_irregular`, `minutesUntilBedtimeTarget` is null, and the owner's
|
|
46
|
+
* `nightFallbackBedtimeLocal` (or 23:00 default) is inside the lead window.
|
|
47
|
+
*
|
|
48
|
+
* `nightFallbackBedtimeLocal` is read from the owner's `nightCheckinTime`
|
|
49
|
+
* profile field by `processSleepCycleCheckins` and threaded in here. It is
|
|
50
|
+
* not consulted for `regular` / `very_regular` owners — those flow through
|
|
51
|
+
* the schedule's bedtime projection exclusively.
|
|
52
|
+
*/
|
|
53
|
+
export declare function shouldRunNightCheckinFromSleepCycle(args: {
|
|
54
|
+
readonly state: CheckinSleepCycleState | null;
|
|
55
|
+
readonly now?: Date;
|
|
56
|
+
readonly nightFallbackBedtimeLocal?: string | null;
|
|
57
|
+
}): boolean;
|
|
58
|
+
/**
|
|
59
|
+
* Project the four sleep-recap fields out of a merged schedule-state record
|
|
60
|
+
* (or any object exposing the same `baseline` / `regularity` shape) so the
|
|
61
|
+
* night-summary prompt can surface them. Returns null when neither a baseline
|
|
62
|
+
* nor a regularity reading is available — the prompt drops the recap
|
|
63
|
+
* section entirely in that case rather than printing "0/100" placeholders.
|
|
64
|
+
*
|
|
65
|
+
* Intentional design: `medianBedtimeLocalHour` and `medianSleepDurationMin`
|
|
66
|
+
* land as null when the baseline is null (fewer than 5 episodes recorded);
|
|
67
|
+
* `sri` and `regularityClass` always have defaults from the regularity
|
|
68
|
+
* scorer (`insufficient_data` + 0) so we surface them when we have anything
|
|
69
|
+
* to show.
|
|
70
|
+
*/
|
|
71
|
+
export declare function buildSleepRecapFromSchedule(schedule: {
|
|
72
|
+
readonly baseline: LifeOpsPersonalBaseline | null;
|
|
73
|
+
readonly regularity: LifeOpsScheduleRegularity;
|
|
74
|
+
} | null): SleepRecap | null;
|
|
75
|
+
//# sourceMappingURL=sleep-cycle-dispatch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sleep-cycle-dispatch.d.ts","sourceRoot":"","sources":["../../src/sleep/sleep-cycle-dispatch.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,qBAAqB,EACrB,uBAAuB,EACvB,sBAAsB,EACtB,yBAAyB,EAC1B,MAAM,wBAAwB,CAAC;AAGhC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnD,eAAO,MAAM,8BAA8B,QAAS,CAAC;AACrD,eAAO,MAAM,0BAA0B,QAAS,CAAC;AAIjD,eAAO,MAAM,+BAA+B,UAAU,CAAC;AAIvD,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,cAAc,EAAE,qBAAqB,CAAC;IAC/C,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,CAAC,EAAE;QACpB,QAAQ,CAAC,eAAe,EAAE,sBAAsB,CAAC;KAClD,CAAC;IACF,QAAQ,CAAC,YAAY,EAAE;QACrB,QAAQ,CAAC,yBAAyB,EAAE,MAAM,GAAG,IAAI,CAAC;KACnD,CAAC;CACH;AAiBD;;;;;;;;;;GAUG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE;IAC7C,QAAQ,CAAC,GAAG,EAAE,IAAI,CAAC;IACnB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B,GAAG,MAAM,GAAG,IAAI,CAyBhB;AAUD,wBAAgB,qCAAqC,CAAC,IAAI,EAAE;IAC1D,QAAQ,CAAC,KAAK,EAAE,sBAAsB,GAAG,IAAI,CAAC;IAC9C,QAAQ,CAAC,GAAG,EAAE,IAAI,CAAC;CACpB,GAAG,OAAO,CAYV;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,mCAAmC,CAAC,IAAI,EAAE;IACxD,QAAQ,CAAC,KAAK,EAAE,sBAAsB,GAAG,IAAI,CAAC;IAC9C,QAAQ,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC;IACpB,QAAQ,CAAC,yBAAyB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACpD,GAAG,OAAO,CAoDV;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE;IACR,QAAQ,CAAC,QAAQ,EAAE,uBAAuB,GAAG,IAAI,CAAC;IAClD,QAAQ,CAAC,UAAU,EAAE,yBAAyB,CAAC;CAChD,GAAG,IAAI,GACP,UAAU,GAAG,IAAI,CAUnB"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { buildUtcDateFromLocalParts, getZonedDateParts } from "../util/time.js";
|
|
2
|
+
import { parseIsoMs } from "../util/time-util.js";
|
|
3
|
+
const MORNING_CHECKIN_WINDOW_MINUTES = 6 * 60;
|
|
4
|
+
const NIGHT_CHECKIN_LEAD_MINUTES = 3 * 60;
|
|
5
|
+
const DEFAULT_IRREGULAR_BEDTIME_LOCAL = "23:00";
|
|
6
|
+
const HHMM_RE = /^(\d{1,2}):(\d{2})$/;
|
|
7
|
+
function parseHHMM(value) {
|
|
8
|
+
if (typeof value !== "string") return null;
|
|
9
|
+
const match = HHMM_RE.exec(value.trim());
|
|
10
|
+
if (!match) return null;
|
|
11
|
+
const [, rawHour = "", rawMinute = ""] = match;
|
|
12
|
+
const hour = Number.parseInt(rawHour, 10);
|
|
13
|
+
const minute = Number.parseInt(rawMinute, 10);
|
|
14
|
+
if (!Number.isFinite(hour) || hour < 0 || hour > 23) return null;
|
|
15
|
+
if (!Number.isFinite(minute) || minute < 0 || minute > 59) return null;
|
|
16
|
+
return { hour, minute };
|
|
17
|
+
}
|
|
18
|
+
function minutesUntilLocalBedtime(args) {
|
|
19
|
+
const parts = parseHHMM(args.localBedtime);
|
|
20
|
+
if (!parts) return null;
|
|
21
|
+
const nowParts = getZonedDateParts(args.now, args.timezone);
|
|
22
|
+
const todayInstant = buildUtcDateFromLocalParts(args.timezone, {
|
|
23
|
+
year: nowParts.year,
|
|
24
|
+
month: nowParts.month,
|
|
25
|
+
day: nowParts.day,
|
|
26
|
+
hour: parts.hour,
|
|
27
|
+
minute: parts.minute,
|
|
28
|
+
second: 0
|
|
29
|
+
}).getTime();
|
|
30
|
+
const nowMs = args.now.getTime();
|
|
31
|
+
const candidateMs = todayInstant >= nowMs ? todayInstant : buildUtcDateFromLocalParts(args.timezone, {
|
|
32
|
+
year: nowParts.year,
|
|
33
|
+
month: nowParts.month,
|
|
34
|
+
day: nowParts.day + 1,
|
|
35
|
+
hour: parts.hour,
|
|
36
|
+
minute: parts.minute,
|
|
37
|
+
second: 0
|
|
38
|
+
}).getTime();
|
|
39
|
+
return Math.round((candidateMs - nowMs) / 6e4);
|
|
40
|
+
}
|
|
41
|
+
function isIrregular(regularityClass) {
|
|
42
|
+
return regularityClass === "irregular" || regularityClass === "very_irregular";
|
|
43
|
+
}
|
|
44
|
+
function shouldRunMorningCheckinFromSleepCycle(args) {
|
|
45
|
+
if (!args.state || args.state.circadianState !== "awake") {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
const wakeAtMs = parseIsoMs(args.state.wakeAt);
|
|
49
|
+
if (wakeAtMs === null) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
const minutesSinceWake = (args.now.getTime() - wakeAtMs) / 6e4;
|
|
53
|
+
return minutesSinceWake >= 0 && minutesSinceWake <= MORNING_CHECKIN_WINDOW_MINUTES;
|
|
54
|
+
}
|
|
55
|
+
function shouldRunNightCheckinFromSleepCycle(args) {
|
|
56
|
+
if (!args.state) {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
if (args.state.circadianState === "winding_down") {
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
if (args.state.circadianState !== "awake" && args.state.circadianState !== "waking") {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
const minutes = args.state.relativeTime.minutesUntilBedtimeTarget;
|
|
66
|
+
if (typeof minutes === "number" && Number.isFinite(minutes) && minutes >= 0 && minutes <= NIGHT_CHECKIN_LEAD_MINUTES) {
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
if (isIrregular(args.state.regularity?.regularityClass) && args.state.timezone && args.now) {
|
|
70
|
+
const fallbackHHMM = args.nightFallbackBedtimeLocal ?? DEFAULT_IRREGULAR_BEDTIME_LOCAL;
|
|
71
|
+
const fallbackMinutes = minutesUntilLocalBedtime({
|
|
72
|
+
now: args.now,
|
|
73
|
+
timezone: args.state.timezone,
|
|
74
|
+
localBedtime: fallbackHHMM
|
|
75
|
+
});
|
|
76
|
+
if (fallbackMinutes !== null && fallbackMinutes >= 0 && fallbackMinutes <= NIGHT_CHECKIN_LEAD_MINUTES) {
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
function buildSleepRecapFromSchedule(schedule) {
|
|
83
|
+
if (!schedule) {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
return {
|
|
87
|
+
medianBedtimeLocalHour: schedule.baseline?.medianBedtimeLocalHour ?? null,
|
|
88
|
+
medianSleepDurationMin: schedule.baseline?.medianSleepDurationMin ?? null,
|
|
89
|
+
sri: schedule.regularity.sri,
|
|
90
|
+
regularityClass: schedule.regularity.regularityClass
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
export {
|
|
94
|
+
DEFAULT_IRREGULAR_BEDTIME_LOCAL,
|
|
95
|
+
MORNING_CHECKIN_WINDOW_MINUTES,
|
|
96
|
+
NIGHT_CHECKIN_LEAD_MINUTES,
|
|
97
|
+
buildSleepRecapFromSchedule,
|
|
98
|
+
minutesUntilLocalBedtime,
|
|
99
|
+
shouldRunMorningCheckinFromSleepCycle,
|
|
100
|
+
shouldRunNightCheckinFromSleepCycle
|
|
101
|
+
};
|
|
102
|
+
//# sourceMappingURL=sleep-cycle-dispatch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/sleep/sleep-cycle-dispatch.ts"],"sourcesContent":["import type {\n LifeOpsCircadianState,\n LifeOpsPersonalBaseline,\n LifeOpsRegularityClass,\n LifeOpsScheduleRegularity,\n} from \"../contracts/health.js\";\nimport { buildUtcDateFromLocalParts, getZonedDateParts } from \"../util/time.js\";\nimport { parseIsoMs } from \"../util/time-util.js\";\nimport type { SleepRecap } from \"./sleep-recap.js\";\n\nexport const MORNING_CHECKIN_WINDOW_MINUTES = 6 * 60;\nexport const NIGHT_CHECKIN_LEAD_MINUTES = 3 * 60;\n// Default bedtime when an irregular-schedule owner has not configured a\n// `nightCheckinTime` profile field. Matches the documented night-summary\n// expectation in the lifeops T9f plan.\nexport const DEFAULT_IRREGULAR_BEDTIME_LOCAL = \"23:00\";\n\nconst HHMM_RE = /^(\\d{1,2}):(\\d{2})$/;\n\nexport interface CheckinSleepCycleState {\n readonly circadianState: LifeOpsCircadianState;\n readonly wakeAt: string | null;\n readonly timezone?: string;\n readonly regularity?: {\n readonly regularityClass: LifeOpsRegularityClass;\n };\n readonly relativeTime: {\n readonly minutesUntilBedtimeTarget: number | null;\n };\n}\n\nfunction parseHHMM(value: string | null | undefined): {\n hour: number;\n minute: number;\n} | null {\n if (typeof value !== \"string\") return null;\n const match = HHMM_RE.exec(value.trim());\n if (!match) return null;\n const [, rawHour = \"\", rawMinute = \"\"] = match;\n const hour = Number.parseInt(rawHour, 10);\n const minute = Number.parseInt(rawMinute, 10);\n if (!Number.isFinite(hour) || hour < 0 || hour > 23) return null;\n if (!Number.isFinite(minute) || minute < 0 || minute > 59) return null;\n return { hour, minute };\n}\n\n/**\n * For owners whose schedule is `irregular` / `very_irregular`, the relative\n * time resolver leaves `bedtimeTargetAt` null because no projection is\n * trustworthy. Without a fallback the night summary never fires for these\n * users. Compute \"minutes until the next occurrence of `localBedtime`\n * (local-time HH:MM) in `timezone`\" — today if still upcoming, otherwise\n * tomorrow.\n *\n * Returns null when inputs are missing or invalid; callers fall back to the\n * normal bedtime-projection path in that case.\n */\nexport function minutesUntilLocalBedtime(args: {\n readonly now: Date;\n readonly timezone: string;\n readonly localBedtime: string;\n}): number | null {\n const parts = parseHHMM(args.localBedtime);\n if (!parts) return null;\n const nowParts = getZonedDateParts(args.now, args.timezone);\n const todayInstant = buildUtcDateFromLocalParts(args.timezone, {\n year: nowParts.year,\n month: nowParts.month,\n day: nowParts.day,\n hour: parts.hour,\n minute: parts.minute,\n second: 0,\n }).getTime();\n const nowMs = args.now.getTime();\n const candidateMs =\n todayInstant >= nowMs\n ? todayInstant\n : buildUtcDateFromLocalParts(args.timezone, {\n year: nowParts.year,\n month: nowParts.month,\n day: nowParts.day + 1,\n hour: parts.hour,\n minute: parts.minute,\n second: 0,\n }).getTime();\n return Math.round((candidateMs - nowMs) / 60_000);\n}\n\nfunction isIrregular(\n regularityClass: LifeOpsRegularityClass | undefined,\n): boolean {\n return (\n regularityClass === \"irregular\" || regularityClass === \"very_irregular\"\n );\n}\n\nexport function shouldRunMorningCheckinFromSleepCycle(args: {\n readonly state: CheckinSleepCycleState | null;\n readonly now: Date;\n}): boolean {\n if (!args.state || args.state.circadianState !== \"awake\") {\n return false;\n }\n const wakeAtMs = parseIsoMs(args.state.wakeAt);\n if (wakeAtMs === null) {\n return false;\n }\n const minutesSinceWake = (args.now.getTime() - wakeAtMs) / 60_000;\n return (\n minutesSinceWake >= 0 && minutesSinceWake <= MORNING_CHECKIN_WINDOW_MINUTES\n );\n}\n\n/**\n * Decide whether the night summary should fire on this scheduler tick.\n *\n * Triggers (any one is sufficient):\n * 1. `circadianState === \"winding_down\"` (early signal from HID-idle / lock).\n * 2. `circadianState` is `awake` or `waking` AND\n * `minutesUntilBedtimeTarget` is inside `NIGHT_CHECKIN_LEAD_MINUTES`.\n * 3. Irregular-owner fallback: `regularityClass` is `irregular` /\n * `very_irregular`, `minutesUntilBedtimeTarget` is null, and the owner's\n * `nightFallbackBedtimeLocal` (or 23:00 default) is inside the lead window.\n *\n * `nightFallbackBedtimeLocal` is read from the owner's `nightCheckinTime`\n * profile field by `processSleepCycleCheckins` and threaded in here. It is\n * not consulted for `regular` / `very_regular` owners — those flow through\n * the schedule's bedtime projection exclusively.\n */\nexport function shouldRunNightCheckinFromSleepCycle(args: {\n readonly state: CheckinSleepCycleState | null;\n readonly now?: Date;\n readonly nightFallbackBedtimeLocal?: string | null;\n}): boolean {\n if (!args.state) {\n return false;\n }\n // `winding_down` is the circadian-rules answer for \"user is winding down\":\n // HID idle >=20m or session locked >=30m outside the overnight window. Treat\n // it as an immediate night-summary trigger so an irregular-schedule owner\n // who winds down at an unusual time still gets the night check-in even when\n // the bedtime-window proximity check below would not fire.\n if (args.state.circadianState === \"winding_down\") {\n return true;\n }\n if (\n args.state.circadianState !== \"awake\" &&\n args.state.circadianState !== \"waking\"\n ) {\n return false;\n }\n const minutes = args.state.relativeTime.minutesUntilBedtimeTarget;\n if (\n typeof minutes === \"number\" &&\n Number.isFinite(minutes) &&\n minutes >= 0 &&\n minutes <= NIGHT_CHECKIN_LEAD_MINUTES\n ) {\n return true;\n }\n // Irregular-owner fallback: relative-time leaves `bedtimeTargetAt` null\n // because there's no trustworthy projection. Use the owner's configured\n // `nightCheckinTime` (or the 23:00 default) so the night summary still\n // fires inside the same NIGHT_CHECKIN_LEAD_MINUTES lead.\n if (\n isIrregular(args.state.regularity?.regularityClass) &&\n args.state.timezone &&\n args.now\n ) {\n const fallbackHHMM =\n args.nightFallbackBedtimeLocal ?? DEFAULT_IRREGULAR_BEDTIME_LOCAL;\n const fallbackMinutes = minutesUntilLocalBedtime({\n now: args.now,\n timezone: args.state.timezone,\n localBedtime: fallbackHHMM,\n });\n if (\n fallbackMinutes !== null &&\n fallbackMinutes >= 0 &&\n fallbackMinutes <= NIGHT_CHECKIN_LEAD_MINUTES\n ) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Project the four sleep-recap fields out of a merged schedule-state record\n * (or any object exposing the same `baseline` / `regularity` shape) so the\n * night-summary prompt can surface them. Returns null when neither a baseline\n * nor a regularity reading is available — the prompt drops the recap\n * section entirely in that case rather than printing \"0/100\" placeholders.\n *\n * Intentional design: `medianBedtimeLocalHour` and `medianSleepDurationMin`\n * land as null when the baseline is null (fewer than 5 episodes recorded);\n * `sri` and `regularityClass` always have defaults from the regularity\n * scorer (`insufficient_data` + 0) so we surface them when we have anything\n * to show.\n */\nexport function buildSleepRecapFromSchedule(\n schedule: {\n readonly baseline: LifeOpsPersonalBaseline | null;\n readonly regularity: LifeOpsScheduleRegularity;\n } | null,\n): SleepRecap | null {\n if (!schedule) {\n return null;\n }\n return {\n medianBedtimeLocalHour: schedule.baseline?.medianBedtimeLocalHour ?? null,\n medianSleepDurationMin: schedule.baseline?.medianSleepDurationMin ?? null,\n sri: schedule.regularity.sri,\n regularityClass: schedule.regularity.regularityClass,\n };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { LifeOpsActivitySignal, LifeOpsDayBoundary, LifeOpsSleepCycle, LifeOpsSleepCycleType } from "../contracts/health.js";
|
|
2
|
+
export type LifeOpsActivityWindow = {
|
|
3
|
+
startMs: number;
|
|
4
|
+
endMs: number;
|
|
5
|
+
source: "app" | "website" | "signal";
|
|
6
|
+
};
|
|
7
|
+
export type LifeOpsSleepEpisode = {
|
|
8
|
+
startMs: number;
|
|
9
|
+
endMs: number | null;
|
|
10
|
+
current: boolean;
|
|
11
|
+
confidence: number;
|
|
12
|
+
source: "health" | "activity_gap";
|
|
13
|
+
observedMs?: number;
|
|
14
|
+
};
|
|
15
|
+
export declare function classifyLifeOpsSleepCycleType(args: {
|
|
16
|
+
startMs: number;
|
|
17
|
+
endMs: number | null;
|
|
18
|
+
nowMs: number;
|
|
19
|
+
timezone: string;
|
|
20
|
+
}): LifeOpsSleepCycleType;
|
|
21
|
+
export interface LifeOpsSleepCycleResolution {
|
|
22
|
+
sleepCycle: LifeOpsSleepCycle;
|
|
23
|
+
sleepEpisodes: LifeOpsSleepEpisode[];
|
|
24
|
+
typicalWakeHour: number | null;
|
|
25
|
+
typicalSleepHour: number | null;
|
|
26
|
+
}
|
|
27
|
+
export declare function resolveLifeOpsSleepCycle(args: {
|
|
28
|
+
nowMs: number;
|
|
29
|
+
timezone: string;
|
|
30
|
+
windows: LifeOpsActivityWindow[];
|
|
31
|
+
signals: LifeOpsActivitySignal[];
|
|
32
|
+
}): LifeOpsSleepCycleResolution;
|
|
33
|
+
export declare function resolveLifeOpsDayBoundary(args: {
|
|
34
|
+
nowMs: number;
|
|
35
|
+
timezone: string;
|
|
36
|
+
sleepCycle: Pick<LifeOpsSleepCycle, "cycleType" | "sleepConfidence" | "currentSleepStartedAt" | "lastSleepStartedAt" | "lastSleepEndedAt">;
|
|
37
|
+
}): LifeOpsDayBoundary;
|
|
38
|
+
//# sourceMappingURL=sleep-cycle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sleep-cycle.d.ts","sourceRoot":"","sources":["../../src/sleep/sleep-cycle.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,qBAAqB,EACrB,kBAAkB,EAElB,iBAAiB,EAEjB,qBAAqB,EACtB,MAAM,wBAAwB,CAAC;AAgBhC,MAAM,MAAM,qBAAqB,GAAG;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,KAAK,GAAG,SAAS,GAAG,QAAQ,CAAC;CACtC,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,QAAQ,GAAG,cAAc,CAAC;IAClC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAuZF,wBAAgB,6BAA6B,CAAC,IAAI,EAAE;IAClD,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;CAClB,GAAG,qBAAqB,CAgBxB;AAeD,MAAM,WAAW,2BAA2B;IAC1C,UAAU,EAAE,iBAAiB,CAAC;IAC9B,aAAa,EAAE,mBAAmB,EAAE,CAAC;IACrC,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE;IAC7C,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,qBAAqB,EAAE,CAAC;IACjC,OAAO,EAAE,qBAAqB,EAAE,CAAC;CAClC,GAAG,2BAA2B,CA2F9B;AAED,wBAAgB,yBAAyB,CAAC,IAAI,EAAE;IAC9C,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,IAAI,CACd,iBAAiB,EACf,WAAW,GACX,iBAAiB,GACjB,uBAAuB,GACvB,oBAAoB,GACpB,kBAAkB,CACrB,CAAC;CACH,GAAG,kBAAkB,CAkDrB"}
|