@mongrov/analytics 0.1.0-alpha.7 → 0.1.0-alpha.9
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/dist/rules/__fakes__/fakeClock.d.ts +7 -0
- package/dist/rules/__fakes__/fakeClock.d.ts.map +1 -0
- package/dist/rules/__fakes__/fakeClock.js +13 -0
- package/dist/rules/__fakes__/fakeClock.js.map +1 -0
- package/dist/rules/__fakes__/fakeEngine.d.ts +12 -0
- package/dist/rules/__fakes__/fakeEngine.d.ts.map +1 -0
- package/dist/rules/__fakes__/fakeEngine.js +33 -0
- package/dist/rules/__fakes__/fakeEngine.js.map +1 -0
- package/dist/rules/__fakes__/fakeStorage.d.ts +7 -0
- package/dist/rules/__fakes__/fakeStorage.d.ts.map +1 -0
- package/dist/rules/__fakes__/fakeStorage.js +22 -0
- package/dist/rules/__fakes__/fakeStorage.js.map +1 -0
- package/dist/rules/compiler-cache.d.ts +16 -0
- package/dist/rules/compiler-cache.d.ts.map +1 -0
- package/dist/rules/compiler-cache.js +29 -0
- package/dist/rules/compiler-cache.js.map +1 -0
- package/dist/rules/compiler.d.ts +27 -0
- package/dist/rules/compiler.d.ts.map +1 -0
- package/dist/rules/compiler.js +210 -0
- package/dist/rules/compiler.js.map +1 -0
- package/dist/rules/defaults/index.d.ts +13 -0
- package/dist/rules/defaults/index.d.ts.map +1 -0
- package/dist/rules/defaults/index.js +13 -0
- package/dist/rules/defaults/index.js.map +1 -0
- package/dist/rules/defaults/loader.d.ts +18 -0
- package/dist/rules/defaults/loader.d.ts.map +1 -0
- package/dist/rules/defaults/loader.js +37 -0
- package/dist/rules/defaults/loader.js.map +1 -0
- package/dist/rules/defaults/luminx.d.ts +6 -0
- package/dist/rules/defaults/luminx.d.ts.map +1 -0
- package/dist/rules/defaults/luminx.js +64 -0
- package/dist/rules/defaults/luminx.js.map +1 -0
- package/dist/rules/defaults/viva.d.ts +6 -0
- package/dist/rules/defaults/viva.d.ts.map +1 -0
- package/dist/rules/defaults/viva.js +45 -0
- package/dist/rules/defaults/viva.js.map +1 -0
- package/dist/rules/defaults/yogaring.d.ts +6 -0
- package/dist/rules/defaults/yogaring.d.ts.map +1 -0
- package/dist/rules/defaults/yogaring.js +45 -0
- package/dist/rules/defaults/yogaring.js.map +1 -0
- package/dist/rules/defaults/ziva.d.ts +10 -0
- package/dist/rules/defaults/ziva.d.ts.map +1 -0
- package/dist/rules/defaults/ziva.js +87 -0
- package/dist/rules/defaults/ziva.js.map +1 -0
- package/dist/rules/emitter.d.ts +20 -0
- package/dist/rules/emitter.d.ts.map +1 -0
- package/dist/rules/emitter.js +32 -0
- package/dist/rules/emitter.js.map +1 -0
- package/dist/rules/evaluator.d.ts +44 -0
- package/dist/rules/evaluator.d.ts.map +1 -0
- package/dist/rules/evaluator.js +127 -0
- package/dist/rules/evaluator.js.map +1 -0
- package/dist/rules/factory.d.ts +13 -0
- package/dist/rules/factory.d.ts.map +1 -0
- package/dist/rules/factory.js +56 -0
- package/dist/rules/factory.js.map +1 -0
- package/dist/rules/hooks/useRuleRegistry.d.ts +16 -0
- package/dist/rules/hooks/useRuleRegistry.d.ts.map +1 -0
- package/dist/rules/hooks/useRuleRegistry.js +26 -0
- package/dist/rules/hooks/useRuleRegistry.js.map +1 -0
- package/dist/rules/hooks/useRuleViolations.d.ts +21 -0
- package/dist/rules/hooks/useRuleViolations.d.ts.map +1 -0
- package/dist/rules/hooks/useRuleViolations.js +32 -0
- package/dist/rules/hooks/useRuleViolations.js.map +1 -0
- package/dist/rules/index.d.ts +30 -1
- package/dist/rules/index.d.ts.map +1 -1
- package/dist/rules/index.js +36 -1
- package/dist/rules/index.js.map +1 -1
- package/dist/rules/registry.d.ts +32 -0
- package/dist/rules/registry.d.ts.map +1 -0
- package/dist/rules/registry.js +98 -0
- package/dist/rules/registry.js.map +1 -0
- package/dist/rules/schema.d.ts +221 -0
- package/dist/rules/schema.d.ts.map +1 -0
- package/dist/rules/schema.js +80 -0
- package/dist/rules/schema.js.map +1 -0
- package/dist/rules/throttle.d.ts +27 -0
- package/dist/rules/throttle.d.ts.map +1 -0
- package/dist/rules/throttle.js +76 -0
- package/dist/rules/throttle.js.map +1 -0
- package/dist/rules/types.d.ts +88 -0
- package/dist/rules/types.d.ts.map +1 -0
- package/dist/rules/types.js +9 -0
- package/dist/rules/types.js.map +1 -0
- package/dist/rules/validator.d.ts +27 -0
- package/dist/rules/validator.d.ts.map +1 -0
- package/dist/rules/validator.js +97 -0
- package/dist/rules/validator.js.map +1 -0
- package/dist/sync/factory.d.ts +8 -0
- package/dist/sync/factory.d.ts.map +1 -1
- package/dist/sync/factory.js +31 -1
- package/dist/sync/factory.js.map +1 -1
- package/dist/sync/flusher.d.ts +6 -0
- package/dist/sync/flusher.d.ts.map +1 -1
- package/dist/sync/flusher.js +13 -1
- package/dist/sync/flusher.js.map +1 -1
- package/dist/tools/__fakes__/engine.d.ts +25 -0
- package/dist/tools/__fakes__/engine.d.ts.map +1 -0
- package/dist/tools/__fakes__/engine.js +37 -0
- package/dist/tools/__fakes__/engine.js.map +1 -0
- package/dist/tools/audit.d.ts +31 -0
- package/dist/tools/audit.d.ts.map +1 -0
- package/dist/tools/audit.js +157 -0
- package/dist/tools/audit.js.map +1 -0
- package/dist/tools/authorize.d.ts +36 -0
- package/dist/tools/authorize.d.ts.map +1 -0
- package/dist/tools/authorize.js +87 -0
- package/dist/tools/authorize.js.map +1 -0
- package/dist/tools/budget.d.ts +15 -0
- package/dist/tools/budget.d.ts.map +1 -0
- package/dist/tools/budget.js +64 -0
- package/dist/tools/budget.js.map +1 -0
- package/dist/tools/factory.d.ts +43 -0
- package/dist/tools/factory.d.ts.map +1 -0
- package/dist/tools/factory.js +137 -0
- package/dist/tools/factory.js.map +1 -0
- package/dist/tools/formatters.d.ts +17 -0
- package/dist/tools/formatters.d.ts.map +1 -0
- package/dist/tools/formatters.js +30 -0
- package/dist/tools/formatters.js.map +1 -0
- package/dist/tools/impls/activity.d.ts +15 -0
- package/dist/tools/impls/activity.d.ts.map +1 -0
- package/dist/tools/impls/activity.js +61 -0
- package/dist/tools/impls/activity.js.map +1 -0
- package/dist/tools/impls/anomaly.d.ts +21 -0
- package/dist/tools/impls/anomaly.d.ts.map +1 -0
- package/dist/tools/impls/anomaly.js +70 -0
- package/dist/tools/impls/anomaly.js.map +1 -0
- package/dist/tools/impls/compare.d.ts +21 -0
- package/dist/tools/impls/compare.d.ts.map +1 -0
- package/dist/tools/impls/compare.js +68 -0
- package/dist/tools/impls/compare.js.map +1 -0
- package/dist/tools/impls/hrv.d.ts +15 -0
- package/dist/tools/impls/hrv.d.ts.map +1 -0
- package/dist/tools/impls/hrv.js +36 -0
- package/dist/tools/impls/hrv.js.map +1 -0
- package/dist/tools/impls/insights.d.ts +18 -0
- package/dist/tools/impls/insights.d.ts.map +1 -0
- package/dist/tools/impls/insights.js +41 -0
- package/dist/tools/impls/insights.js.map +1 -0
- package/dist/tools/impls/sleep.d.ts +15 -0
- package/dist/tools/impls/sleep.d.ts.map +1 -0
- package/dist/tools/impls/sleep.js +43 -0
- package/dist/tools/impls/sleep.js.map +1 -0
- package/dist/tools/index.d.ts +13 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +21 -1
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/rate-limit.d.ts +26 -0
- package/dist/tools/rate-limit.d.ts.map +1 -0
- package/dist/tools/rate-limit.js +67 -0
- package/dist/tools/rate-limit.js.map +1 -0
- package/dist/tools/types.d.ts +121 -0
- package/dist/tools/types.d.ts.map +1 -0
- package/dist/tools/types.js +16 -0
- package/dist/tools/types.js.map +1 -0
- package/dist/tools/wrap.d.ts +53 -0
- package/dist/tools/wrap.d.ts.map +1 -0
- package/dist/tools/wrap.js +135 -0
- package/dist/tools/wrap.js.map +1 -0
- package/package.json +5 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"activity.d.ts","sourceRoot":"","sources":["../../../src/tools/impls/activity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,OAAO,KAAK,EAAE,QAAQ,EAAc,MAAM,UAAU,CAAA;AAEpD,eAAO,MAAM,2BAA2B;;;;;;;;;EAGtC,CAAA;AAEF,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CACzC,OAAO,2BAA2B,CACnC,CAAA;AAaD,eAAO,MAAM,gBAAgB,EAAE,QAAQ,CAAC,qBAAqB,CAiE5D,CAAA"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { formatBytes } from '../formatters';
|
|
3
|
+
export const getActivityTotalInputSchema = z.object({
|
|
4
|
+
userId: z.string(),
|
|
5
|
+
days: z.number().int().min(1).max(90),
|
|
6
|
+
});
|
|
7
|
+
export const getActivityTotal = async (input, ctx) => {
|
|
8
|
+
const params = {
|
|
9
|
+
userId: input.userId,
|
|
10
|
+
brand: ctx.brand,
|
|
11
|
+
familyId: ctx.familyId,
|
|
12
|
+
days: input.days,
|
|
13
|
+
};
|
|
14
|
+
const steps = await ctx.analytics.execute(`SELECT date_trunc('day', ts)::VARCHAR AS day, SUM(steps)::INTEGER AS steps
|
|
15
|
+
FROM activity
|
|
16
|
+
WHERE user_id = $userId AND brand = $brand AND family_id = $familyId
|
|
17
|
+
AND ts >= now() - INTERVAL ($days) DAY
|
|
18
|
+
GROUP BY 1 ORDER BY 1`, params);
|
|
19
|
+
const buckets = await ctx.analytics.execute(`SELECT date_trunc('day', ts)::VARCHAR AS day,
|
|
20
|
+
SUM(calories)::DOUBLE AS calories,
|
|
21
|
+
SUM(distance_km)::DOUBLE AS distance_km
|
|
22
|
+
FROM activity_bucket
|
|
23
|
+
WHERE user_id = $userId AND brand = $brand AND family_id = $familyId
|
|
24
|
+
AND ts >= now() - INTERVAL ($days) DAY
|
|
25
|
+
GROUP BY 1 ORDER BY 1`, params);
|
|
26
|
+
if (steps.length === 0 && buckets.length === 0) {
|
|
27
|
+
return finalize('No activity data for the requested window.', 0);
|
|
28
|
+
}
|
|
29
|
+
const bucketByDay = new Map(buckets.map(b => [b.day, b]));
|
|
30
|
+
const days = new Set([
|
|
31
|
+
...steps.map(s => s.day),
|
|
32
|
+
...buckets.map(b => b.day),
|
|
33
|
+
]);
|
|
34
|
+
const orderedDays = Array.from(days).sort();
|
|
35
|
+
let totalSteps = 0;
|
|
36
|
+
let totalCalories = 0;
|
|
37
|
+
let totalDistance = 0;
|
|
38
|
+
const perDay = [];
|
|
39
|
+
for (const day of orderedDays) {
|
|
40
|
+
const s = steps.find(r => r.day === day)?.steps ?? 0;
|
|
41
|
+
const b = bucketByDay.get(day);
|
|
42
|
+
totalSteps += s;
|
|
43
|
+
totalCalories += b?.calories ?? 0;
|
|
44
|
+
totalDistance += b?.distance_km ?? 0;
|
|
45
|
+
const parts = [`${s} steps`];
|
|
46
|
+
if (b?.calories != null)
|
|
47
|
+
parts.push(`${b.calories.toFixed(0)} kcal`);
|
|
48
|
+
if (b?.distance_km != null)
|
|
49
|
+
parts.push(`${b.distance_km.toFixed(2)} km`);
|
|
50
|
+
perDay.push(` ${day}: ${parts.join(', ')}`);
|
|
51
|
+
}
|
|
52
|
+
const text = `Activity, last ${input.days} days:\n${perDay.join('\n')}\n`
|
|
53
|
+
+ ` Totals: ${totalSteps} steps, `
|
|
54
|
+
+ `${totalCalories.toFixed(0)} kcal, `
|
|
55
|
+
+ `${totalDistance.toFixed(2)} km`;
|
|
56
|
+
return finalize(text, orderedDays.length);
|
|
57
|
+
};
|
|
58
|
+
function finalize(text, rowCount) {
|
|
59
|
+
return { text, rowCount, bytes: formatBytes(text) };
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=activity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"activity.js","sourceRoot":"","sources":["../../../src/tools/impls/activity.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { ToolImpl } from '../types';
|
|
3
|
+
export declare const detectAnomalyInputSchema: z.ZodObject<{
|
|
4
|
+
userId: z.ZodString;
|
|
5
|
+
metric: z.ZodEnum<["hrv_ms", "sleep_total_minutes", "activity_steps"]>;
|
|
6
|
+
lookbackDays: z.ZodNumber;
|
|
7
|
+
stddevThreshold: z.ZodDefault<z.ZodNumber>;
|
|
8
|
+
}, "strip", z.ZodTypeAny, {
|
|
9
|
+
metric: "hrv_ms" | "activity_steps" | "sleep_total_minutes";
|
|
10
|
+
userId: string;
|
|
11
|
+
lookbackDays: number;
|
|
12
|
+
stddevThreshold: number;
|
|
13
|
+
}, {
|
|
14
|
+
metric: "hrv_ms" | "activity_steps" | "sleep_total_minutes";
|
|
15
|
+
userId: string;
|
|
16
|
+
lookbackDays: number;
|
|
17
|
+
stddevThreshold?: number | undefined;
|
|
18
|
+
}>;
|
|
19
|
+
export type DetectAnomalyInput = z.infer<typeof detectAnomalyInputSchema>;
|
|
20
|
+
export declare const detectAnomaly: ToolImpl<DetectAnomalyInput>;
|
|
21
|
+
//# sourceMappingURL=anomaly.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anomaly.d.ts","sourceRoot":"","sources":["../../../src/tools/impls/anomaly.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,OAAO,KAAK,EAAE,QAAQ,EAAc,MAAM,UAAU,CAAA;AAEpD,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;EAKnC,CAAA;AAEF,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAA;AA+CzE,eAAO,MAAM,aAAa,EAAE,QAAQ,CAAC,kBAAkB,CA4CtD,CAAA"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { formatBytes, popStddev } from '../formatters';
|
|
3
|
+
export const detectAnomalyInputSchema = z.object({
|
|
4
|
+
userId: z.string(),
|
|
5
|
+
metric: z.enum(['hrv_ms', 'sleep_total_minutes', 'activity_steps']),
|
|
6
|
+
lookbackDays: z.number().int().min(7).max(90),
|
|
7
|
+
stddevThreshold: z.number().min(1).max(4).default(2),
|
|
8
|
+
});
|
|
9
|
+
const METRIC_SPECS = {
|
|
10
|
+
hrv_ms: {
|
|
11
|
+
sql: lookbackDays => `SELECT date_trunc('day', ts)::VARCHAR AS day, AVG(hrv_ms)::DOUBLE AS value
|
|
12
|
+
FROM hrv
|
|
13
|
+
WHERE user_id = $userId AND brand = $brand AND family_id = $familyId
|
|
14
|
+
AND hrv_ms IS NOT NULL
|
|
15
|
+
AND ts >= now() - INTERVAL (${lookbackDays}) DAY
|
|
16
|
+
GROUP BY 1 ORDER BY 1`,
|
|
17
|
+
unit: 'ms',
|
|
18
|
+
label: 'HRV',
|
|
19
|
+
},
|
|
20
|
+
sleep_total_minutes: {
|
|
21
|
+
sql: lookbackDays => `SELECT night_of::VARCHAR AS day, SUM(total_minutes)::DOUBLE AS value
|
|
22
|
+
FROM sleep_session
|
|
23
|
+
WHERE user_id = $userId AND brand = $brand AND family_id = $familyId
|
|
24
|
+
AND ts_start >= now() - INTERVAL (${lookbackDays}) DAY
|
|
25
|
+
GROUP BY night_of ORDER BY night_of`,
|
|
26
|
+
unit: 'min',
|
|
27
|
+
label: 'sleep total',
|
|
28
|
+
},
|
|
29
|
+
activity_steps: {
|
|
30
|
+
sql: lookbackDays => `SELECT date_trunc('day', ts)::VARCHAR AS day, SUM(steps)::DOUBLE AS value
|
|
31
|
+
FROM activity
|
|
32
|
+
WHERE user_id = $userId AND brand = $brand AND family_id = $familyId
|
|
33
|
+
AND ts >= now() - INTERVAL (${lookbackDays}) DAY
|
|
34
|
+
GROUP BY 1 ORDER BY 1`,
|
|
35
|
+
unit: 'steps',
|
|
36
|
+
label: 'activity',
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
export const detectAnomaly = async (input, ctx) => {
|
|
40
|
+
const spec = METRIC_SPECS[input.metric];
|
|
41
|
+
const rows = await ctx.analytics.execute(spec.sql(input.lookbackDays), {
|
|
42
|
+
userId: input.userId,
|
|
43
|
+
brand: ctx.brand,
|
|
44
|
+
familyId: ctx.familyId,
|
|
45
|
+
});
|
|
46
|
+
if (rows.length === 0) {
|
|
47
|
+
return finalize(`No ${spec.label} data for the last ${input.lookbackDays} days.`, 0);
|
|
48
|
+
}
|
|
49
|
+
const values = rows.map(r => r.value);
|
|
50
|
+
const mean = values.reduce((a, v) => a + v, 0) / values.length;
|
|
51
|
+
const stddev = popStddev(values);
|
|
52
|
+
const gate = input.stddevThreshold * stddev;
|
|
53
|
+
const outliers = rows.filter(r => Math.abs(r.value - mean) > gate);
|
|
54
|
+
const outlierText = outliers.length === 0
|
|
55
|
+
? ' No outliers detected.'
|
|
56
|
+
: outliers
|
|
57
|
+
.map(r => ` ${r.day}: ${r.value.toFixed(1)}${spec.unit} `
|
|
58
|
+
+ `(${(r.value - mean).toFixed(1)}${spec.unit} from mean)`)
|
|
59
|
+
.join('\n');
|
|
60
|
+
const text = `${spec.label} anomalies (last ${input.lookbackDays}d, `
|
|
61
|
+
+ `threshold ${input.stddevThreshold}σ):\n`
|
|
62
|
+
+ ` baseline: mean ${mean.toFixed(1)}${spec.unit}, `
|
|
63
|
+
+ `stddev ${stddev.toFixed(1)}${spec.unit}\n`
|
|
64
|
+
+ `${outlierText}`;
|
|
65
|
+
return finalize(text, outliers.length);
|
|
66
|
+
};
|
|
67
|
+
function finalize(text, rowCount) {
|
|
68
|
+
return { text, rowCount, bytes: formatBytes(text) };
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=anomaly.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anomaly.js","sourceRoot":"","sources":["../../../src/tools/impls/anomaly.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { ToolImpl } from '../types';
|
|
3
|
+
export declare const compareTrendInputSchema: z.ZodObject<{
|
|
4
|
+
userId: z.ZodString;
|
|
5
|
+
metric: z.ZodEnum<["hrv_ms", "sleep_total_minutes", "activity_steps"]>;
|
|
6
|
+
currentWindowDays: z.ZodNumber;
|
|
7
|
+
priorWindowDays: z.ZodNumber;
|
|
8
|
+
}, "strip", z.ZodTypeAny, {
|
|
9
|
+
metric: "hrv_ms" | "activity_steps" | "sleep_total_minutes";
|
|
10
|
+
userId: string;
|
|
11
|
+
currentWindowDays: number;
|
|
12
|
+
priorWindowDays: number;
|
|
13
|
+
}, {
|
|
14
|
+
metric: "hrv_ms" | "activity_steps" | "sleep_total_minutes";
|
|
15
|
+
userId: string;
|
|
16
|
+
currentWindowDays: number;
|
|
17
|
+
priorWindowDays: number;
|
|
18
|
+
}>;
|
|
19
|
+
export type CompareTrendInput = z.infer<typeof compareTrendInputSchema>;
|
|
20
|
+
export declare const compareTrend: ToolImpl<CompareTrendInput>;
|
|
21
|
+
//# sourceMappingURL=compare.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compare.d.ts","sourceRoot":"","sources":["../../../src/tools/impls/compare.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,OAAO,KAAK,EAAE,QAAQ,EAAc,MAAM,UAAU,CAAA;AAEpD,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;EAKlC,CAAA;AAEF,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAA;AAiDvE,eAAO,MAAM,YAAY,EAAE,QAAQ,CAAC,iBAAiB,CAuCpD,CAAA"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { deltaPct, formatBytes } from '../formatters';
|
|
3
|
+
export const compareTrendInputSchema = z.object({
|
|
4
|
+
userId: z.string(),
|
|
5
|
+
metric: z.enum(['hrv_ms', 'sleep_total_minutes', 'activity_steps']),
|
|
6
|
+
currentWindowDays: z.number().int().min(1).max(30),
|
|
7
|
+
priorWindowDays: z.number().int().min(1).max(30),
|
|
8
|
+
});
|
|
9
|
+
const METRIC_SPECS = {
|
|
10
|
+
hrv_ms: {
|
|
11
|
+
sql: (windowDays, offsetDays) => `SELECT AVG(hrv_ms)::DOUBLE AS value
|
|
12
|
+
FROM hrv
|
|
13
|
+
WHERE user_id = $userId AND brand = $brand AND family_id = $familyId
|
|
14
|
+
AND hrv_ms IS NOT NULL
|
|
15
|
+
AND ts >= now() - INTERVAL (${windowDays + offsetDays}) DAY
|
|
16
|
+
AND ts < now() - INTERVAL (${offsetDays}) DAY`,
|
|
17
|
+
unit: 'ms',
|
|
18
|
+
label: 'HRV',
|
|
19
|
+
},
|
|
20
|
+
sleep_total_minutes: {
|
|
21
|
+
sql: (windowDays, offsetDays) => `SELECT AVG(total_minutes)::DOUBLE AS value
|
|
22
|
+
FROM sleep_session
|
|
23
|
+
WHERE user_id = $userId AND brand = $brand AND family_id = $familyId
|
|
24
|
+
AND ts_start >= now() - INTERVAL (${windowDays + offsetDays}) DAY
|
|
25
|
+
AND ts_start < now() - INTERVAL (${offsetDays}) DAY`,
|
|
26
|
+
unit: 'min',
|
|
27
|
+
label: 'sleep total',
|
|
28
|
+
},
|
|
29
|
+
activity_steps: {
|
|
30
|
+
sql: (windowDays, offsetDays) => `SELECT AVG(daily_steps)::DOUBLE AS value FROM (
|
|
31
|
+
SELECT date_trunc('day', ts) AS day, SUM(steps) AS daily_steps
|
|
32
|
+
FROM activity
|
|
33
|
+
WHERE user_id = $userId AND brand = $brand AND family_id = $familyId
|
|
34
|
+
AND ts >= now() - INTERVAL (${windowDays + offsetDays}) DAY
|
|
35
|
+
AND ts < now() - INTERVAL (${offsetDays}) DAY
|
|
36
|
+
GROUP BY 1
|
|
37
|
+
)`,
|
|
38
|
+
unit: 'steps/day',
|
|
39
|
+
label: 'activity',
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
export const compareTrend = async (input, ctx) => {
|
|
43
|
+
const spec = METRIC_SPECS[input.metric];
|
|
44
|
+
const params = {
|
|
45
|
+
userId: input.userId,
|
|
46
|
+
brand: ctx.brand,
|
|
47
|
+
familyId: ctx.familyId,
|
|
48
|
+
};
|
|
49
|
+
const currentRows = await ctx.analytics.execute(spec.sql(input.currentWindowDays, 0), params);
|
|
50
|
+
const priorRows = await ctx.analytics.execute(spec.sql(input.priorWindowDays, input.currentWindowDays), params);
|
|
51
|
+
const current = currentRows[0]?.value ?? null;
|
|
52
|
+
const prior = priorRows[0]?.value ?? null;
|
|
53
|
+
if (current == null && prior == null) {
|
|
54
|
+
return finalize(`Insufficient ${spec.label} data to compare the requested windows.`, 0);
|
|
55
|
+
}
|
|
56
|
+
const text = `${spec.label} trend (${input.metric}):\n`
|
|
57
|
+
+ ` current ${input.currentWindowDays}d: ${fmt(current)}${spec.unit}\n`
|
|
58
|
+
+ ` prior ${input.priorWindowDays}d: ${fmt(prior)}${spec.unit}\n`
|
|
59
|
+
+ ` delta: ${current != null && prior != null ? deltaPct(current, prior) : 'n/a'}`;
|
|
60
|
+
return finalize(text, (current == null ? 0 : 1) + (prior == null ? 0 : 1));
|
|
61
|
+
};
|
|
62
|
+
function fmt(v) {
|
|
63
|
+
return v == null ? 'n/a' : v.toFixed(1);
|
|
64
|
+
}
|
|
65
|
+
function finalize(text, rowCount) {
|
|
66
|
+
return { text, rowCount, bytes: formatBytes(text) };
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=compare.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compare.js","sourceRoot":"","sources":["../../../src/tools/impls/compare.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAGrD,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC9C,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE;IAClB,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,qBAAqB,EAAE,gBAAgB,CAAC,CAAC;IACnE,iBAAiB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC;IAClD,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC;CACjD,CAAC,CAAA;AAcF,MAAM,YAAY,GAAoD;IACpE,MAAM,EAAE;QACN,GAAG,EAAE,CAAC,UAAU,EAAE,UAAU,EAAE,EAAE,CAC9B;;;;uCAIiC,UAAU,GAAG,UAAU;uCACvB,UAAU,OAAO;QACpD,IAAI,EAAE,IAAI;QACV,KAAK,EAAE,KAAK;KACb;IACD,mBAAmB,EAAE;QACnB,GAAG,EAAE,CAAC,UAAU,EAAE,UAAU,EAAE,EAAE,CAC9B;;;6CAGuC,UAAU,GAAG,UAAU;6CACvB,UAAU,OAAO;QAC1D,IAAI,EAAE,KAAK;QACX,KAAK,EAAE,aAAa;KACrB;IACD,cAAc,EAAE;QACd,GAAG,EAAE,CAAC,UAAU,EAAE,UAAU,EAAE,EAAE,CAC9B;;;;yCAImC,UAAU,GAAG,UAAU;yCACvB,UAAU;;SAE1C;QACL,IAAI,EAAE,WAAW;QACjB,KAAK,EAAE,UAAU;KAClB;CACF,CAAA;AAED,MAAM,CAAC,MAAM,YAAY,GAAgC,KAAK,EAC5D,KAAK,EACL,GAAG,EACH,EAAE;IACF,MAAM,IAAI,GAAG,YAAY,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;IACvC,MAAM,MAAM,GAAG;QACb,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,KAAK,EAAE,GAAG,CAAC,KAAK;QAChB,QAAQ,EAAE,GAAG,CAAC,QAAQ;KACvB,CAAA;IAED,MAAM,WAAW,GAAG,MAAM,GAAG,CAAC,SAAS,CAAC,OAAO,CAC7C,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,iBAAiB,EAAE,CAAC,CAAC,EACpC,MAAM,CACP,CAAA;IACD,MAAM,SAAS,GAAG,MAAM,GAAG,CAAC,SAAS,CAAC,OAAO,CAC3C,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,eAAe,EAAE,KAAK,CAAC,iBAAiB,CAAC,EACxD,MAAM,CACP,CAAA;IAED,MAAM,OAAO,GAAG,WAAW,CAAC,CAAC,CAAC,EAAE,KAAK,IAAI,IAAI,CAAA;IAC7C,MAAM,KAAK,GAAG,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,IAAI,IAAI,CAAA;IAEzC,IAAI,OAAO,IAAI,IAAI,IAAI,KAAK,IAAI,IAAI,EAAE,CAAC;QACrC,OAAO,QAAQ,CACb,gBAAgB,IAAI,CAAC,KAAK,yCAAyC,EACnE,CAAC,CACF,CAAA;IACH,CAAC;IAED,MAAM,IAAI,GACN,GAAG,IAAI,CAAC,KAAK,WAAW,KAAK,CAAC,MAAM,MAAM;UAC1C,aAAa,KAAK,CAAC,iBAAiB,MAAM,GAAG,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,IAAI,IAAI;UACtE,WAAW,KAAK,CAAC,eAAe,QAAQ,GAAG,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,IAAI,IAAI;UAClE,YACA,OAAO,IAAI,IAAI,IAAI,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,KAChE,EAAE,CAAA;IAEJ,OAAO,QAAQ,CAAC,IAAI,EAAE,CAAC,OAAO,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;AAC5E,CAAC,CAAA;AAED,SAAS,GAAG,CAAC,CAAgB;IAC3B,OAAO,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAA;AACzC,CAAC;AAED,SAAS,QAAQ,CAAC,IAAY,EAAE,QAAgB;IAC9C,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,CAAC,IAAI,CAAC,EAAE,CAAA;AACrD,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { ToolImpl } from '../types';
|
|
3
|
+
export declare const getHRVInputSchema: z.ZodObject<{
|
|
4
|
+
userId: z.ZodString;
|
|
5
|
+
days: z.ZodNumber;
|
|
6
|
+
}, "strip", z.ZodTypeAny, {
|
|
7
|
+
days: number;
|
|
8
|
+
userId: string;
|
|
9
|
+
}, {
|
|
10
|
+
days: number;
|
|
11
|
+
userId: string;
|
|
12
|
+
}>;
|
|
13
|
+
export type GetHRVInput = z.infer<typeof getHRVInputSchema>;
|
|
14
|
+
export declare const getHRV: ToolImpl<GetHRVInput>;
|
|
15
|
+
//# sourceMappingURL=hrv.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hrv.d.ts","sourceRoot":"","sources":["../../../src/tools/impls/hrv.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,OAAO,KAAK,EAAE,QAAQ,EAAc,MAAM,UAAU,CAAA;AAEpD,eAAO,MAAM,iBAAiB;;;;;;;;;EAG5B,CAAA;AAEF,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAA;AAO3D,eAAO,MAAM,MAAM,EAAE,QAAQ,CAAC,WAAW,CAiCxC,CAAA"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { deltaPct, formatBytes } from '../formatters';
|
|
3
|
+
export const getHRVInputSchema = z.object({
|
|
4
|
+
userId: z.string(),
|
|
5
|
+
days: z.number().int().min(1).max(90),
|
|
6
|
+
});
|
|
7
|
+
export const getHRV = async (input, ctx) => {
|
|
8
|
+
const rows = await ctx.analytics.execute(`SELECT date_trunc('day', ts)::VARCHAR AS day, AVG(hrv_ms)::DOUBLE AS avg_hrv
|
|
9
|
+
FROM hrv
|
|
10
|
+
WHERE user_id = $userId AND brand = $brand AND family_id = $familyId
|
|
11
|
+
AND ts >= now() - INTERVAL ($days) DAY
|
|
12
|
+
AND hrv_ms IS NOT NULL
|
|
13
|
+
GROUP BY 1 ORDER BY 1`, {
|
|
14
|
+
userId: input.userId,
|
|
15
|
+
brand: ctx.brand,
|
|
16
|
+
familyId: ctx.familyId,
|
|
17
|
+
days: input.days,
|
|
18
|
+
});
|
|
19
|
+
if (rows.length === 0) {
|
|
20
|
+
return finalize('No HRV data for the requested window.', 0);
|
|
21
|
+
}
|
|
22
|
+
const values = rows.map(r => r.avg_hrv);
|
|
23
|
+
const baseline = values.reduce((a, v) => a + v, 0) / values.length;
|
|
24
|
+
const latest = values[values.length - 1];
|
|
25
|
+
const daily = rows
|
|
26
|
+
.map(r => ` ${r.day}: ${r.avg_hrv.toFixed(1)}ms`)
|
|
27
|
+
.join('\n');
|
|
28
|
+
const text = `HRV, last ${input.days} days:\n${daily}\n`
|
|
29
|
+
+ ` ${input.days}-day avg: ${baseline.toFixed(1)}ms `
|
|
30
|
+
+ `(latest ${latest.toFixed(1)}ms, ${deltaPct(latest, baseline)} vs avg)`;
|
|
31
|
+
return finalize(text, rows.length);
|
|
32
|
+
};
|
|
33
|
+
function finalize(text, rowCount) {
|
|
34
|
+
return { text, rowCount, bytes: formatBytes(text) };
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=hrv.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hrv.js","sourceRoot":"","sources":["../../../src/tools/impls/hrv.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAGrD,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC;IACxC,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE;IAClB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC;CACtC,CAAC,CAAA;AASF,MAAM,CAAC,MAAM,MAAM,GAA0B,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;IAChE,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,SAAS,CAAC,OAAO,CACtC;;;;;2BAKuB,EACvB;QACE,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,KAAK,EAAE,GAAG,CAAC,KAAK;QAChB,QAAQ,EAAE,GAAG,CAAC,QAAQ;QACtB,IAAI,EAAE,KAAK,CAAC,IAAI;KACjB,CACF,CAAA;IAED,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtB,OAAO,QAAQ,CAAC,uCAAuC,EAAE,CAAC,CAAC,CAAA;IAC7D,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAA;IACvC,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,CAAA;IAClE,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAA;IACxC,MAAM,KAAK,GAAG,IAAI;SACf,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;SACjD,IAAI,CAAC,IAAI,CAAC,CAAA;IAEb,MAAM,IAAI,GACN,aAAa,KAAK,CAAC,IAAI,WAAW,KAAK,IAAI;UAC3C,KAAK,KAAK,CAAC,IAAI,aAAa,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK;UACpD,WAAW,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,UAAU,CAAA;IAE3E,OAAO,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,CAAA;AACpC,CAAC,CAAA;AAED,SAAS,QAAQ,CAAC,IAAY,EAAE,QAAgB;IAC9C,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,CAAC,IAAI,CAAC,EAAE,CAAA;AACrD,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { ToolImpl } from '../types';
|
|
3
|
+
export declare const getInsightsInputSchema: z.ZodObject<{
|
|
4
|
+
userId: z.ZodString;
|
|
5
|
+
days: z.ZodDefault<z.ZodNumber>;
|
|
6
|
+
severity: z.ZodOptional<z.ZodEnum<["info", "warn", "critical"]>>;
|
|
7
|
+
}, "strip", z.ZodTypeAny, {
|
|
8
|
+
days: number;
|
|
9
|
+
userId: string;
|
|
10
|
+
severity?: "info" | "warn" | "critical" | undefined;
|
|
11
|
+
}, {
|
|
12
|
+
userId: string;
|
|
13
|
+
days?: number | undefined;
|
|
14
|
+
severity?: "info" | "warn" | "critical" | undefined;
|
|
15
|
+
}>;
|
|
16
|
+
export type GetInsightsInput = z.infer<typeof getInsightsInputSchema>;
|
|
17
|
+
export declare const getInsights: ToolImpl<GetInsightsInput>;
|
|
18
|
+
//# sourceMappingURL=insights.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"insights.d.ts","sourceRoot":"","sources":["../../../src/tools/impls/insights.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,OAAO,KAAK,EAAE,QAAQ,EAAc,MAAM,UAAU,CAAA;AAEpD,eAAO,MAAM,sBAAsB;;;;;;;;;;;;EAIjC,CAAA;AAEF,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAA;AAUrE,eAAO,MAAM,WAAW,EAAE,QAAQ,CAAC,gBAAgB,CAuClD,CAAA"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { formatBytes } from '../formatters';
|
|
3
|
+
export const getInsightsInputSchema = z.object({
|
|
4
|
+
userId: z.string(),
|
|
5
|
+
days: z.number().int().min(1).max(30).default(7),
|
|
6
|
+
severity: z.enum(['info', 'warn', 'critical']).optional(),
|
|
7
|
+
});
|
|
8
|
+
export const getInsights = async (input, ctx) => {
|
|
9
|
+
const params = {
|
|
10
|
+
userId: input.userId,
|
|
11
|
+
brand: ctx.brand,
|
|
12
|
+
familyId: ctx.familyId,
|
|
13
|
+
days: input.days,
|
|
14
|
+
};
|
|
15
|
+
let severityClause = '';
|
|
16
|
+
if (input.severity) {
|
|
17
|
+
severityClause = ' AND severity = $severity';
|
|
18
|
+
params.severity = input.severity;
|
|
19
|
+
}
|
|
20
|
+
const rows = await ctx.analytics.execute(`SELECT id, ts::VARCHAR AS ts, severity, title, body
|
|
21
|
+
FROM insight
|
|
22
|
+
WHERE user_id = $userId AND brand = $brand AND family_id = $familyId
|
|
23
|
+
AND ts >= now() - INTERVAL ($days) DAY${severityClause}
|
|
24
|
+
ORDER BY ts DESC`, params);
|
|
25
|
+
if (rows.length === 0) {
|
|
26
|
+
const scope = input.severity ? ` (${input.severity})` : '';
|
|
27
|
+
return finalize(`No insights${scope} in the last ${input.days} days.`, 0);
|
|
28
|
+
}
|
|
29
|
+
const bullets = rows
|
|
30
|
+
.map((r) => {
|
|
31
|
+
const body = r.body ? ` — ${r.body}` : '';
|
|
32
|
+
return ` [${r.severity}] ${r.ts}: ${r.title}${body}`;
|
|
33
|
+
})
|
|
34
|
+
.join('\n');
|
|
35
|
+
const text = `Insights, last ${input.days} days:\n${bullets}`;
|
|
36
|
+
return finalize(text, rows.length);
|
|
37
|
+
};
|
|
38
|
+
function finalize(text, rowCount) {
|
|
39
|
+
return { text, rowCount, bytes: formatBytes(text) };
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=insights.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"insights.js","sourceRoot":"","sources":["../../../src/tools/impls/insights.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAG3C,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC7C,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE;IAClB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IAChD,QAAQ,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC,QAAQ,EAAE;CAC1D,CAAC,CAAA;AAYF,MAAM,CAAC,MAAM,WAAW,GAA+B,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;IAC1E,MAAM,MAAM,GAA4B;QACtC,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,KAAK,EAAE,GAAG,CAAC,KAAK;QAChB,QAAQ,EAAE,GAAG,CAAC,QAAQ;QACtB,IAAI,EAAE,KAAK,CAAC,IAAI;KACjB,CAAA;IACD,IAAI,cAAc,GAAG,EAAE,CAAA;IACvB,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;QACnB,cAAc,GAAG,2BAA2B,CAAA;QAC5C,MAAM,CAAC,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAA;IAClC,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,SAAS,CAAC,OAAO,CACtC;;;+CAG2C,cAAc;sBACvC,EAClB,MAAM,CACP,CAAA;IAED,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtB,MAAM,KAAK,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC,EAAE,CAAA;QAC1D,OAAO,QAAQ,CACb,cAAc,KAAK,gBAAgB,KAAK,CAAC,IAAI,QAAQ,EACrD,CAAC,CACF,CAAA;IACH,CAAC;IAED,MAAM,OAAO,GAAG,IAAI;SACjB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QACT,MAAM,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAA;QACzC,OAAO,MAAM,CAAC,CAAC,QAAQ,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,KAAK,GAAG,IAAI,EAAE,CAAA;IACvD,CAAC,CAAC;SACD,IAAI,CAAC,IAAI,CAAC,CAAA;IAEb,MAAM,IAAI,GAAG,kBAAkB,KAAK,CAAC,IAAI,WAAW,OAAO,EAAE,CAAA;IAC7D,OAAO,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,CAAA;AACpC,CAAC,CAAA;AAED,SAAS,QAAQ,CAAC,IAAY,EAAE,QAAgB;IAC9C,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,CAAC,IAAI,CAAC,EAAE,CAAA;AACrD,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { ToolImpl } from '../types';
|
|
3
|
+
export declare const getSleepSummaryInputSchema: z.ZodObject<{
|
|
4
|
+
userId: z.ZodString;
|
|
5
|
+
days: z.ZodNumber;
|
|
6
|
+
}, "strip", z.ZodTypeAny, {
|
|
7
|
+
days: number;
|
|
8
|
+
userId: string;
|
|
9
|
+
}, {
|
|
10
|
+
days: number;
|
|
11
|
+
userId: string;
|
|
12
|
+
}>;
|
|
13
|
+
export type GetSleepSummaryInput = z.infer<typeof getSleepSummaryInputSchema>;
|
|
14
|
+
export declare const getSleepSummary: ToolImpl<GetSleepSummaryInput>;
|
|
15
|
+
//# sourceMappingURL=sleep.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sleep.d.ts","sourceRoot":"","sources":["../../../src/tools/impls/sleep.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,OAAO,KAAK,EAAE,QAAQ,EAAc,MAAM,UAAU,CAAA;AAEpD,eAAO,MAAM,0BAA0B;;;;;;;;;EAGrC,CAAA;AAEF,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAA;AAU7E,eAAO,MAAM,eAAe,EAAE,QAAQ,CAAC,oBAAoB,CA0C1D,CAAA"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { formatBytes } from '../formatters';
|
|
3
|
+
export const getSleepSummaryInputSchema = z.object({
|
|
4
|
+
userId: z.string(),
|
|
5
|
+
days: z.number().int().min(1).max(90),
|
|
6
|
+
});
|
|
7
|
+
export const getSleepSummary = async (input, ctx) => {
|
|
8
|
+
const rows = await ctx.analytics.execute(`SELECT night_of::VARCHAR AS night_of,
|
|
9
|
+
SUM(total_minutes)::INTEGER AS total_minutes,
|
|
10
|
+
SUM(deep_minutes)::INTEGER AS deep_minutes,
|
|
11
|
+
SUM(rem_minutes)::INTEGER AS rem_minutes,
|
|
12
|
+
SUM(light_minutes)::INTEGER AS light_minutes
|
|
13
|
+
FROM sleep_session
|
|
14
|
+
WHERE user_id = $userId AND brand = $brand AND family_id = $familyId
|
|
15
|
+
AND ts_start >= now() - INTERVAL ($days) DAY
|
|
16
|
+
GROUP BY night_of ORDER BY night_of`, {
|
|
17
|
+
userId: input.userId,
|
|
18
|
+
brand: ctx.brand,
|
|
19
|
+
familyId: ctx.familyId,
|
|
20
|
+
days: input.days,
|
|
21
|
+
});
|
|
22
|
+
if (rows.length === 0) {
|
|
23
|
+
return finalize('No sleep sessions for the requested window.', 0);
|
|
24
|
+
}
|
|
25
|
+
const totalMean = rows.reduce((a, r) => a + r.total_minutes, 0) / rows.length;
|
|
26
|
+
const perNight = rows
|
|
27
|
+
.map((r) => {
|
|
28
|
+
const parts = [`${r.total_minutes}m total`];
|
|
29
|
+
if (r.deep_minutes != null)
|
|
30
|
+
parts.push(`${r.deep_minutes}m deep`);
|
|
31
|
+
if (r.rem_minutes != null)
|
|
32
|
+
parts.push(`${r.rem_minutes}m REM`);
|
|
33
|
+
return ` ${r.night_of}: ${parts.join(', ')}`;
|
|
34
|
+
})
|
|
35
|
+
.join('\n');
|
|
36
|
+
const text = `Sleep, last ${input.days} days:\n${perNight}\n`
|
|
37
|
+
+ ` ${rows.length}-night avg total: ${totalMean.toFixed(0)}m`;
|
|
38
|
+
return finalize(text, rows.length);
|
|
39
|
+
};
|
|
40
|
+
function finalize(text, rowCount) {
|
|
41
|
+
return { text, rowCount, bytes: formatBytes(text) };
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=sleep.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sleep.js","sourceRoot":"","sources":["../../../src/tools/impls/sleep.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAG3C,MAAM,CAAC,MAAM,0BAA0B,GAAG,CAAC,CAAC,MAAM,CAAC;IACjD,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE;IAClB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC;CACtC,CAAC,CAAA;AAYF,MAAM,CAAC,MAAM,eAAe,GAAmC,KAAK,EAClE,KAAK,EACL,GAAG,EACH,EAAE;IACF,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,SAAS,CAAC,OAAO,CACtC;;;;;;;;yCAQqC,EACrC;QACE,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,KAAK,EAAE,GAAG,CAAC,KAAK;QAChB,QAAQ,EAAE,GAAG,CAAC,QAAQ;QACtB,IAAI,EAAE,KAAK,CAAC,IAAI;KACjB,CACF,CAAA;IAED,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtB,OAAO,QAAQ,CAAC,6CAA6C,EAAE,CAAC,CAAC,CAAA;IACnE,CAAC;IAED,MAAM,SAAS,GACX,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,aAAa,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,CAAA;IAC/D,MAAM,QAAQ,GAAG,IAAI;SAClB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QACT,MAAM,KAAK,GAAa,CAAC,GAAG,CAAC,CAAC,aAAa,SAAS,CAAC,CAAA;QACrD,IAAI,CAAC,CAAC,YAAY,IAAI,IAAI;YAAE,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,YAAY,QAAQ,CAAC,CAAA;QACjE,IAAI,CAAC,CAAC,WAAW,IAAI,IAAI;YAAE,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,WAAW,OAAO,CAAC,CAAA;QAC9D,OAAO,KAAK,CAAC,CAAC,QAAQ,KAAK,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAA;IAC/C,CAAC,CAAC;SACD,IAAI,CAAC,IAAI,CAAC,CAAA;IAEb,MAAM,IAAI,GACN,eAAe,KAAK,CAAC,IAAI,WAAW,QAAQ,IAAI;UAChD,KAAK,IAAI,CAAC,MAAM,qBAAqB,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAA;IAEhE,OAAO,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,CAAA;AACpC,CAAC,CAAA;AAED,SAAS,QAAQ,CAAC,IAAY,EAAE,QAAgB;IAC9C,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,CAAC,IAAI,CAAC,EAAE,CAAA;AACrD,CAAC"}
|
package/dist/tools/index.d.ts
CHANGED
|
@@ -1,2 +1,14 @@
|
|
|
1
|
-
export
|
|
1
|
+
export * from './audit';
|
|
2
|
+
export * from './authorize';
|
|
3
|
+
export * from './budget';
|
|
4
|
+
export * from './factory';
|
|
5
|
+
export * from './rate-limit';
|
|
6
|
+
export * from './types';
|
|
7
|
+
export * from './wrap';
|
|
8
|
+
export { getHRV, getHRVInputSchema, type GetHRVInput, } from './impls/hrv';
|
|
9
|
+
export { getSleepSummary, getSleepSummaryInputSchema, type GetSleepSummaryInput, } from './impls/sleep';
|
|
10
|
+
export { getActivityTotal, getActivityTotalInputSchema, type GetActivityTotalInput, } from './impls/activity';
|
|
11
|
+
export { compareTrend, compareTrendInputSchema, type CompareTrendInput, } from './impls/compare';
|
|
12
|
+
export { detectAnomaly, detectAnomalyInputSchema, type DetectAnomalyInput, } from './impls/anomaly';
|
|
13
|
+
export { getInsights, getInsightsInputSchema, type GetInsightsInput, } from './impls/insights';
|
|
2
14
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AASA,cAAc,SAAS,CAAA;AACvB,cAAc,aAAa,CAAA;AAC3B,cAAc,UAAU,CAAA;AACxB,cAAc,WAAW,CAAA;AACzB,cAAc,cAAc,CAAA;AAC5B,cAAc,SAAS,CAAA;AACvB,cAAc,QAAQ,CAAA;AAEtB,OAAO,EACL,MAAM,EACN,iBAAiB,EACjB,KAAK,WAAW,GACjB,MAAM,aAAa,CAAA;AACpB,OAAO,EACL,eAAe,EACf,0BAA0B,EAC1B,KAAK,oBAAoB,GAC1B,MAAM,eAAe,CAAA;AACtB,OAAO,EACL,gBAAgB,EAChB,2BAA2B,EAC3B,KAAK,qBAAqB,GAC3B,MAAM,kBAAkB,CAAA;AACzB,OAAO,EACL,YAAY,EACZ,uBAAuB,EACvB,KAAK,iBAAiB,GACvB,MAAM,iBAAiB,CAAA;AACxB,OAAO,EACL,aAAa,EACb,wBAAwB,EACxB,KAAK,kBAAkB,GACxB,MAAM,iBAAiB,CAAA;AACxB,OAAO,EACL,WAAW,EACX,sBAAsB,EACtB,KAAK,gBAAgB,GACtB,MAAM,kBAAkB,CAAA"}
|
package/dist/tools/index.js
CHANGED
|
@@ -1,2 +1,22 @@
|
|
|
1
|
-
|
|
1
|
+
// @mongrov/analytics/tools — typed AI SDK tools + MCP.
|
|
2
|
+
//
|
|
3
|
+
// v0.1.0-alpha (T-01..T-12 landed): tool impls, formatters, rate
|
|
4
|
+
// limiter, authorize hooks (with optional familyMembersProvider),
|
|
5
|
+
// batched `tool_call_audit` writer, output-budget helper, `makeTool`
|
|
6
|
+
// wrapper composing rate → auth → execute → budget → audit, and the
|
|
7
|
+
// `createAnalyticsTools` factory returning an AI SDK tool map.
|
|
8
|
+
// MCP subpath (T-13/T-14) still pending.
|
|
9
|
+
export * from './audit';
|
|
10
|
+
export * from './authorize';
|
|
11
|
+
export * from './budget';
|
|
12
|
+
export * from './factory';
|
|
13
|
+
export * from './rate-limit';
|
|
14
|
+
export * from './types';
|
|
15
|
+
export * from './wrap';
|
|
16
|
+
export { getHRV, getHRVInputSchema, } from './impls/hrv';
|
|
17
|
+
export { getSleepSummary, getSleepSummaryInputSchema, } from './impls/sleep';
|
|
18
|
+
export { getActivityTotal, getActivityTotalInputSchema, } from './impls/activity';
|
|
19
|
+
export { compareTrend, compareTrendInputSchema, } from './impls/compare';
|
|
20
|
+
export { detectAnomaly, detectAnomalyInputSchema, } from './impls/anomaly';
|
|
21
|
+
export { getInsights, getInsightsInputSchema, } from './impls/insights';
|
|
2
22
|
//# sourceMappingURL=index.js.map
|
package/dist/tools/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":""}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA,uDAAuD;AACvD,EAAE;AACF,iEAAiE;AACjE,kEAAkE;AAClE,qEAAqE;AACrE,oEAAoE;AACpE,+DAA+D;AAC/D,yCAAyC;AAEzC,cAAc,SAAS,CAAA;AACvB,cAAc,aAAa,CAAA;AAC3B,cAAc,UAAU,CAAA;AACxB,cAAc,WAAW,CAAA;AACzB,cAAc,cAAc,CAAA;AAC5B,cAAc,SAAS,CAAA;AACvB,cAAc,QAAQ,CAAA;AAEtB,OAAO,EACL,MAAM,EACN,iBAAiB,GAElB,MAAM,aAAa,CAAA;AACpB,OAAO,EACL,eAAe,EACf,0BAA0B,GAE3B,MAAM,eAAe,CAAA;AACtB,OAAO,EACL,gBAAgB,EAChB,2BAA2B,GAE5B,MAAM,kBAAkB,CAAA;AACzB,OAAO,EACL,YAAY,EACZ,uBAAuB,GAExB,MAAM,iBAAiB,CAAA;AACxB,OAAO,EACL,aAAa,EACb,wBAAwB,GAEzB,MAAM,iBAAiB,CAAA;AACxB,OAAO,EACL,WAAW,EACX,sBAAsB,GAEvB,MAAM,kBAAkB,CAAA"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory token-bucket rate limiter for analytics tools.
|
|
3
|
+
*
|
|
4
|
+
* Three independent buckets per `(toolName, userId)`:
|
|
5
|
+
* - per-tool-per-minute
|
|
6
|
+
* - per-tool-per-hour
|
|
7
|
+
* - per-user-per-minute (across all tools)
|
|
8
|
+
*
|
|
9
|
+
* `check(toolName, userId)` refills every bucket by elapsed time,
|
|
10
|
+
* then attempts to spend one token from all three. If any lacks a
|
|
11
|
+
* token, returns `false` **without** spending from the others.
|
|
12
|
+
* Otherwise deducts one from each and returns `true`.
|
|
13
|
+
*
|
|
14
|
+
* Persistence is out of scope for v0.1.0 — process restart resets
|
|
15
|
+
* buckets. Audit persistence (T-09) handles the durable trail.
|
|
16
|
+
*/
|
|
17
|
+
import { type RateLimitConfig } from './types';
|
|
18
|
+
export interface RateLimiter {
|
|
19
|
+
check: (toolName: string, userId: string) => boolean;
|
|
20
|
+
}
|
|
21
|
+
export interface CreateRateLimiterConfig {
|
|
22
|
+
config?: RateLimitConfig;
|
|
23
|
+
clock?: () => number;
|
|
24
|
+
}
|
|
25
|
+
export declare function createRateLimiter(cfg?: CreateRateLimiterConfig): RateLimiter;
|
|
26
|
+
//# sourceMappingURL=rate-limit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../../src/tools/rate-limit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAsB,KAAK,eAAe,EAAE,MAAM,SAAS,CAAA;AAUlE,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,OAAO,CAAA;CACrD;AAED,MAAM,WAAW,uBAAuB;IACtC,MAAM,CAAC,EAAE,eAAe,CAAA;IACxB,KAAK,CAAC,EAAE,MAAM,MAAM,CAAA;CACrB;AAKD,wBAAgB,iBAAiB,CAC/B,GAAG,GAAE,uBAA4B,GAChC,WAAW,CA6Eb"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory token-bucket rate limiter for analytics tools.
|
|
3
|
+
*
|
|
4
|
+
* Three independent buckets per `(toolName, userId)`:
|
|
5
|
+
* - per-tool-per-minute
|
|
6
|
+
* - per-tool-per-hour
|
|
7
|
+
* - per-user-per-minute (across all tools)
|
|
8
|
+
*
|
|
9
|
+
* `check(toolName, userId)` refills every bucket by elapsed time,
|
|
10
|
+
* then attempts to spend one token from all three. If any lacks a
|
|
11
|
+
* token, returns `false` **without** spending from the others.
|
|
12
|
+
* Otherwise deducts one from each and returns `true`.
|
|
13
|
+
*
|
|
14
|
+
* Persistence is out of scope for v0.1.0 — process restart resets
|
|
15
|
+
* buckets. Audit persistence (T-09) handles the durable trail.
|
|
16
|
+
*/
|
|
17
|
+
import { DEFAULT_RATE_LIMIT } from './types';
|
|
18
|
+
const MS_PER_MINUTE = 60000;
|
|
19
|
+
const MS_PER_HOUR = 3600000;
|
|
20
|
+
export function createRateLimiter(cfg = {}) {
|
|
21
|
+
const rl = cfg.config ?? DEFAULT_RATE_LIMIT;
|
|
22
|
+
const clock = cfg.clock ?? Date.now;
|
|
23
|
+
const perToolMinute = new Map();
|
|
24
|
+
const perToolHour = new Map();
|
|
25
|
+
const perUserMinute = new Map();
|
|
26
|
+
function getOrCreate(map, key, capacity, windowMs, now) {
|
|
27
|
+
let b = map.get(key);
|
|
28
|
+
if (!b) {
|
|
29
|
+
b = {
|
|
30
|
+
tokens: capacity,
|
|
31
|
+
lastRefillMs: now,
|
|
32
|
+
capacity,
|
|
33
|
+
refillPerMs: capacity / windowMs,
|
|
34
|
+
};
|
|
35
|
+
map.set(key, b);
|
|
36
|
+
}
|
|
37
|
+
return b;
|
|
38
|
+
}
|
|
39
|
+
function refill(b, now) {
|
|
40
|
+
const elapsed = now - b.lastRefillMs;
|
|
41
|
+
if (elapsed <= 0)
|
|
42
|
+
return;
|
|
43
|
+
b.tokens = Math.min(b.capacity, b.tokens + elapsed * b.refillPerMs);
|
|
44
|
+
b.lastRefillMs = now;
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
check(toolName, userId) {
|
|
48
|
+
const now = clock();
|
|
49
|
+
const toolKey = `${toolName}:${userId}`;
|
|
50
|
+
const userKey = userId;
|
|
51
|
+
const tm = getOrCreate(perToolMinute, toolKey, rl.perToolPerMinute, MS_PER_MINUTE, now);
|
|
52
|
+
const th = getOrCreate(perToolHour, toolKey, rl.perToolPerHour, MS_PER_HOUR, now);
|
|
53
|
+
const um = getOrCreate(perUserMinute, userKey, rl.perUserPerMinute, MS_PER_MINUTE, now);
|
|
54
|
+
refill(tm, now);
|
|
55
|
+
refill(th, now);
|
|
56
|
+
refill(um, now);
|
|
57
|
+
if (tm.tokens < 1 || th.tokens < 1 || um.tokens < 1) {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
tm.tokens -= 1;
|
|
61
|
+
th.tokens -= 1;
|
|
62
|
+
um.tokens -= 1;
|
|
63
|
+
return true;
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=rate-limit.js.map
|