@mongrov/analytics 0.5.0 → 0.7.0
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 +23 -10
- package/UPGRADING.md +370 -0
- package/dist/core/device-events.d.ts +14 -0
- package/dist/core/device-events.d.ts.map +1 -0
- package/dist/core/device-events.js +13 -0
- package/dist/core/device-events.js.map +1 -0
- package/dist/core/errors.d.ts +1 -1
- package/dist/core/errors.d.ts.map +1 -1
- package/dist/core/errors.js.map +1 -1
- package/dist/core/extensions.d.ts +21 -4
- package/dist/core/extensions.d.ts.map +1 -1
- package/dist/core/extensions.js +21 -4
- package/dist/core/extensions.js.map +1 -1
- package/dist/core/factory.d.ts.map +1 -1
- package/dist/core/factory.js +124 -18
- package/dist/core/factory.js.map +1 -1
- package/dist/core/hooks.js +1 -1
- package/dist/core/hooks.js.map +1 -1
- package/dist/core/index.d.ts +11 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +14 -0
- package/dist/core/index.js.map +1 -1
- package/dist/core/insight.d.ts +44 -0
- package/dist/core/insight.d.ts.map +1 -0
- package/dist/core/insight.js +62 -0
- package/dist/core/insight.js.map +1 -0
- package/dist/core/machine.d.ts.map +1 -1
- package/dist/core/machine.js +9 -1
- package/dist/core/machine.js.map +1 -1
- package/dist/core/metric_metadata.d.ts +57 -2
- package/dist/core/metric_metadata.d.ts.map +1 -1
- package/dist/core/metric_metadata.js +36 -8
- package/dist/core/metric_metadata.js.map +1 -1
- package/dist/core/migrations.d.ts +6 -0
- package/dist/core/migrations.d.ts.map +1 -1
- package/dist/core/migrations.js +92 -4
- package/dist/core/migrations.js.map +1 -1
- package/dist/core/retention.d.ts +44 -13
- package/dist/core/retention.d.ts.map +1 -1
- package/dist/core/retention.js +50 -22
- package/dist/core/retention.js.map +1 -1
- package/dist/core/schemas.d.ts +74 -3
- package/dist/core/schemas.d.ts.map +1 -1
- package/dist/core/schemas.js +168 -5
- package/dist/core/schemas.js.map +1 -1
- package/dist/core/table_metadata.d.ts.map +1 -1
- package/dist/core/table_metadata.js +4 -0
- package/dist/core/table_metadata.js.map +1 -1
- package/dist/core/types.d.ts +45 -10
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/warehouse.d.ts +28 -0
- package/dist/core/warehouse.d.ts.map +1 -1
- package/dist/core/warehouse.js +44 -0
- package/dist/core/warehouse.js.map +1 -1
- package/dist/core/watermark-cache.d.ts +75 -0
- package/dist/core/watermark-cache.d.ts.map +1 -0
- package/dist/core/watermark-cache.js +0 -0
- package/dist/core/watermark-cache.js.map +1 -0
- package/dist/rules/__fakes__/fakeEngine.d.ts +2 -0
- package/dist/rules/__fakes__/fakeEngine.d.ts.map +1 -1
- package/dist/rules/__fakes__/fakeEngine.js +4 -0
- package/dist/rules/__fakes__/fakeEngine.js.map +1 -1
- package/dist/rules/baseline-reader.d.ts +55 -0
- package/dist/rules/baseline-reader.d.ts.map +1 -0
- package/dist/rules/baseline-reader.js +111 -0
- package/dist/rules/baseline-reader.js.map +1 -0
- package/dist/rules/compiler.d.ts +36 -1
- package/dist/rules/compiler.d.ts.map +1 -1
- package/dist/rules/compiler.js +185 -39
- package/dist/rules/compiler.js.map +1 -1
- package/dist/rules/defaults/luminx.d.ts.map +1 -1
- package/dist/rules/defaults/luminx.js +8 -0
- package/dist/rules/defaults/luminx.js.map +1 -1
- package/dist/rules/defaults/viva.d.ts.map +1 -1
- package/dist/rules/defaults/viva.js +7 -0
- package/dist/rules/defaults/viva.js.map +1 -1
- package/dist/rules/defaults/yogaring.d.ts.map +1 -1
- package/dist/rules/defaults/yogaring.js +7 -0
- package/dist/rules/defaults/yogaring.js.map +1 -1
- package/dist/rules/defaults/ziva.d.ts.map +1 -1
- package/dist/rules/defaults/ziva.js +100 -24
- package/dist/rules/defaults/ziva.js.map +1 -1
- package/dist/rules/evaluator.d.ts +14 -3
- package/dist/rules/evaluator.d.ts.map +1 -1
- package/dist/rules/evaluator.js +157 -5
- package/dist/rules/evaluator.js.map +1 -1
- package/dist/rules/factory.d.ts.map +1 -1
- package/dist/rules/factory.js +5 -1
- package/dist/rules/factory.js.map +1 -1
- package/dist/rules/index.d.ts +2 -1
- package/dist/rules/index.d.ts.map +1 -1
- package/dist/rules/index.js +2 -1
- package/dist/rules/index.js.map +1 -1
- package/dist/rules/schema.d.ts +78 -7
- package/dist/rules/schema.d.ts.map +1 -1
- package/dist/rules/schema.js +44 -0
- package/dist/rules/schema.js.map +1 -1
- package/dist/rules/types.d.ts +16 -1
- package/dist/rules/types.d.ts.map +1 -1
- package/dist/rules/validator.d.ts.map +1 -1
- package/dist/rules/validator.js +56 -0
- package/dist/rules/validator.js.map +1 -1
- package/dist/sync/baseline-compute.d.ts +63 -0
- package/dist/sync/baseline-compute.d.ts.map +1 -0
- package/dist/sync/baseline-compute.js +211 -0
- package/dist/sync/baseline-compute.js.map +1 -0
- package/dist/sync/events.d.ts +19 -5
- package/dist/sync/events.d.ts.map +1 -1
- package/dist/sync/events.js +17 -0
- package/dist/sync/events.js.map +1 -1
- package/dist/sync/factory.d.ts +25 -4
- package/dist/sync/factory.d.ts.map +1 -1
- package/dist/sync/factory.js +109 -15
- package/dist/sync/factory.js.map +1 -1
- package/dist/sync/fetcher.d.ts +15 -2
- package/dist/sync/fetcher.d.ts.map +1 -1
- package/dist/sync/fetcher.js +59 -11
- package/dist/sync/fetcher.js.map +1 -1
- package/dist/sync/flusher.d.ts +52 -2
- package/dist/sync/flusher.d.ts.map +1 -1
- package/dist/sync/flusher.js +78 -9
- package/dist/sync/flusher.js.map +1 -1
- package/dist/sync/index.d.ts +4 -2
- package/dist/sync/index.d.ts.map +1 -1
- package/dist/sync/index.js +1 -0
- package/dist/sync/index.js.map +1 -1
- package/dist/sync/mapper/activity.d.ts.map +1 -1
- package/dist/sync/mapper/activity.js +2 -1
- package/dist/sync/mapper/activity.js.map +1 -1
- package/dist/sync/mapper/battery.d.ts +14 -7
- package/dist/sync/mapper/battery.d.ts.map +1 -1
- package/dist/sync/mapper/battery.js +13 -7
- package/dist/sync/mapper/battery.js.map +1 -1
- package/dist/sync/mapper/firmware.d.ts.map +1 -1
- package/dist/sync/mapper/firmware.js +4 -1
- package/dist/sync/mapper/firmware.js.map +1 -1
- package/dist/sync/mapper/ring-config.js +1 -1
- package/dist/sync/mapper/ring-config.js.map +1 -1
- package/dist/sync/mapper/schema.d.ts +411 -0
- package/dist/sync/mapper/schema.d.ts.map +1 -0
- package/dist/sync/mapper/schema.js +128 -0
- package/dist/sync/mapper/schema.js.map +1 -0
- package/dist/sync/mapper/sleep.d.ts +58 -13
- package/dist/sync/mapper/sleep.d.ts.map +1 -1
- package/dist/sync/mapper/sleep.js +141 -44
- package/dist/sync/mapper/sleep.js.map +1 -1
- package/dist/sync/mapper/types.d.ts +75 -10
- package/dist/sync/mapper/types.d.ts.map +1 -1
- package/dist/sync/scheduler.d.ts +7 -0
- package/dist/sync/scheduler.d.ts.map +1 -1
- package/dist/sync/scheduler.js +14 -0
- package/dist/sync/scheduler.js.map +1 -1
- package/dist/tools/__fakes__/engine.d.ts +2 -0
- package/dist/tools/__fakes__/engine.d.ts.map +1 -1
- package/dist/tools/__fakes__/engine.js +10 -0
- package/dist/tools/__fakes__/engine.js.map +1 -1
- package/dist/tools/authorize.d.ts +19 -12
- package/dist/tools/authorize.d.ts.map +1 -1
- package/dist/tools/authorize.js +31 -40
- package/dist/tools/authorize.js.map +1 -1
- package/dist/tools/factory.d.ts +2 -0
- package/dist/tools/factory.d.ts.map +1 -1
- package/dist/tools/factory.js +12 -1
- package/dist/tools/factory.js.map +1 -1
- package/dist/tools/formatters/copy-guidelines.d.ts +57 -0
- package/dist/tools/formatters/copy-guidelines.d.ts.map +1 -0
- package/dist/tools/formatters/copy-guidelines.js +110 -0
- package/dist/tools/formatters/copy-guidelines.js.map +1 -0
- package/dist/tools/formatters/index.d.ts +14 -0
- package/dist/tools/formatters/index.d.ts.map +1 -0
- package/dist/tools/formatters/index.js +14 -0
- package/dist/tools/formatters/index.js.map +1 -0
- package/dist/tools/formatters/spo2.d.ts +21 -0
- package/dist/tools/formatters/spo2.d.ts.map +1 -0
- package/dist/tools/formatters/spo2.js +79 -0
- package/dist/tools/formatters/spo2.js.map +1 -0
- package/dist/tools/{formatters.d.ts → formatters/text.d.ts} +1 -1
- package/dist/tools/formatters/text.d.ts.map +1 -0
- package/dist/tools/{formatters.js → formatters/text.js} +1 -1
- package/dist/tools/formatters/text.js.map +1 -0
- package/dist/tools/impls/activity.d.ts +2 -2
- package/dist/tools/impls/activity.js +7 -3
- package/dist/tools/impls/activity.js.map +1 -1
- package/dist/tools/impls/anomaly.d.ts +2 -2
- package/dist/tools/impls/anomaly.js +8 -4
- package/dist/tools/impls/anomaly.js.map +1 -1
- package/dist/tools/impls/compare.d.ts +2 -2
- package/dist/tools/impls/compare.js +8 -4
- package/dist/tools/impls/compare.js.map +1 -1
- package/dist/tools/impls/hrv.d.ts +2 -2
- package/dist/tools/impls/hrv.js +6 -2
- package/dist/tools/impls/hrv.js.map +1 -1
- package/dist/tools/impls/insights.d.ts +4 -4
- package/dist/tools/impls/insights.d.ts.map +1 -1
- package/dist/tools/impls/insights.js +10 -3
- package/dist/tools/impls/insights.js.map +1 -1
- package/dist/tools/impls/sleep.d.ts +2 -2
- package/dist/tools/impls/sleep.js +6 -2
- package/dist/tools/impls/sleep.js.map +1 -1
- package/dist/tools/impls/spo2.d.ts +52 -0
- package/dist/tools/impls/spo2.d.ts.map +1 -0
- package/dist/tools/impls/spo2.js +65 -0
- package/dist/tools/impls/spo2.js.map +1 -0
- package/dist/tools/index.d.ts +1 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +4 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/mcp/guard.d.ts +28 -11
- package/dist/tools/mcp/guard.d.ts.map +1 -1
- package/dist/tools/mcp/guard.js +45 -15
- package/dist/tools/mcp/guard.js.map +1 -1
- package/dist/tools/mcp/index.d.ts +1 -1
- package/dist/tools/mcp/index.d.ts.map +1 -1
- package/dist/tools/mcp/index.js +1 -1
- package/dist/tools/mcp/index.js.map +1 -1
- package/dist/tools/mcp/server.d.ts.map +1 -1
- package/dist/tools/mcp/server.js +4 -0
- package/dist/tools/mcp/server.js.map +1 -1
- package/dist/tools/mcp/transports/http.d.ts.map +1 -1
- package/dist/tools/mcp/transports/http.js +4 -0
- package/dist/tools/mcp/transports/http.js.map +1 -1
- package/package.json +8 -5
- package/dist/tools/formatters.d.ts.map +0 -1
- package/dist/tools/formatters.js.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import { formatBytes, popStddev } from '../formatters';
|
|
2
|
+
import { assertNoBanTerms, formatBytes, popStddev } from '../formatters';
|
|
3
3
|
export const detectAnomalyInputSchema = z.object({
|
|
4
4
|
userId: z.string(),
|
|
5
5
|
metric: z.enum(['hrv_ms', 'sleep_total_minutes', 'activity_steps']),
|
|
@@ -9,7 +9,7 @@ export const detectAnomalyInputSchema = z.object({
|
|
|
9
9
|
const METRIC_SPECS = {
|
|
10
10
|
hrv_ms: {
|
|
11
11
|
sql: lookbackDays => `SELECT date_trunc('day', ts)::VARCHAR AS day, AVG(hrv_ms)::DOUBLE AS value
|
|
12
|
-
FROM
|
|
12
|
+
FROM v_hrv
|
|
13
13
|
WHERE user_id = $userId AND brand = $brand AND family_id = $familyId
|
|
14
14
|
AND hrv_ms IS NOT NULL
|
|
15
15
|
AND ts >= now() - INTERVAL (${lookbackDays}) DAY
|
|
@@ -19,7 +19,7 @@ const METRIC_SPECS = {
|
|
|
19
19
|
},
|
|
20
20
|
sleep_total_minutes: {
|
|
21
21
|
sql: lookbackDays => `SELECT night_of::VARCHAR AS day, SUM(total_minutes)::DOUBLE AS value
|
|
22
|
-
FROM
|
|
22
|
+
FROM v_sleep_session
|
|
23
23
|
WHERE user_id = $userId AND brand = $brand AND family_id = $familyId
|
|
24
24
|
AND ts_start >= now() - INTERVAL (${lookbackDays}) DAY
|
|
25
25
|
GROUP BY night_of ORDER BY night_of`,
|
|
@@ -28,7 +28,7 @@ const METRIC_SPECS = {
|
|
|
28
28
|
},
|
|
29
29
|
activity_steps: {
|
|
30
30
|
sql: lookbackDays => `SELECT date_trunc('day', ts)::VARCHAR AS day, SUM(steps)::DOUBLE AS value
|
|
31
|
-
FROM
|
|
31
|
+
FROM v_activity
|
|
32
32
|
WHERE user_id = $userId AND brand = $brand AND family_id = $familyId
|
|
33
33
|
AND ts >= now() - INTERVAL (${lookbackDays}) DAY
|
|
34
34
|
GROUP BY 1 ORDER BY 1`,
|
|
@@ -65,6 +65,10 @@ export const detectAnomaly = async (input, ctx) => {
|
|
|
65
65
|
return finalize(text, outliers.length);
|
|
66
66
|
};
|
|
67
67
|
function finalize(text, rowCount) {
|
|
68
|
+
// principle 37 — every formatter's return path is guarded, not
|
|
69
|
+
// just the SpO2 one. Tool text lands in an LLM's context, and the
|
|
70
|
+
// model repeats whatever register it finds there.
|
|
71
|
+
assertNoBanTerms(text, 'detectAnomaly');
|
|
68
72
|
return { text, rowCount, bytes: formatBytes(text) };
|
|
69
73
|
}
|
|
70
74
|
//# sourceMappingURL=anomaly.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"anomaly.js","sourceRoot":"","sources":["../../../src/tools/impls/anomaly.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"anomaly.js","sourceRoot":"","sources":["../../../src/tools/impls/anomaly.ts"],"names":[],"mappings":"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"}
|
|
@@ -6,13 +6,13 @@ export declare const compareTrendInputSchema: z.ZodObject<{
|
|
|
6
6
|
currentWindowDays: z.ZodNumber;
|
|
7
7
|
priorWindowDays: z.ZodNumber;
|
|
8
8
|
}, "strip", z.ZodTypeAny, {
|
|
9
|
-
metric: "hrv_ms" | "activity_steps" | "sleep_total_minutes";
|
|
10
9
|
userId: string;
|
|
10
|
+
metric: "hrv_ms" | "activity_steps" | "sleep_total_minutes";
|
|
11
11
|
currentWindowDays: number;
|
|
12
12
|
priorWindowDays: number;
|
|
13
13
|
}, {
|
|
14
|
-
metric: "hrv_ms" | "activity_steps" | "sleep_total_minutes";
|
|
15
14
|
userId: string;
|
|
15
|
+
metric: "hrv_ms" | "activity_steps" | "sleep_total_minutes";
|
|
16
16
|
currentWindowDays: number;
|
|
17
17
|
priorWindowDays: number;
|
|
18
18
|
}>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import { deltaPct, formatBytes } from '../formatters';
|
|
2
|
+
import { assertNoBanTerms, deltaPct, formatBytes } from '../formatters';
|
|
3
3
|
export const compareTrendInputSchema = z.object({
|
|
4
4
|
userId: z.string(),
|
|
5
5
|
metric: z.enum(['hrv_ms', 'sleep_total_minutes', 'activity_steps']),
|
|
@@ -9,7 +9,7 @@ export const compareTrendInputSchema = z.object({
|
|
|
9
9
|
const METRIC_SPECS = {
|
|
10
10
|
hrv_ms: {
|
|
11
11
|
sql: (windowDays, offsetDays) => `SELECT AVG(hrv_ms)::DOUBLE AS value
|
|
12
|
-
FROM
|
|
12
|
+
FROM v_hrv
|
|
13
13
|
WHERE user_id = $userId AND brand = $brand AND family_id = $familyId
|
|
14
14
|
AND hrv_ms IS NOT NULL
|
|
15
15
|
AND ts >= now() - INTERVAL (${windowDays + offsetDays}) DAY
|
|
@@ -19,7 +19,7 @@ const METRIC_SPECS = {
|
|
|
19
19
|
},
|
|
20
20
|
sleep_total_minutes: {
|
|
21
21
|
sql: (windowDays, offsetDays) => `SELECT AVG(total_minutes)::DOUBLE AS value
|
|
22
|
-
FROM
|
|
22
|
+
FROM v_sleep_session
|
|
23
23
|
WHERE user_id = $userId AND brand = $brand AND family_id = $familyId
|
|
24
24
|
AND ts_start >= now() - INTERVAL (${windowDays + offsetDays}) DAY
|
|
25
25
|
AND ts_start < now() - INTERVAL (${offsetDays}) DAY`,
|
|
@@ -29,7 +29,7 @@ const METRIC_SPECS = {
|
|
|
29
29
|
activity_steps: {
|
|
30
30
|
sql: (windowDays, offsetDays) => `SELECT AVG(daily_steps)::DOUBLE AS value FROM (
|
|
31
31
|
SELECT date_trunc('day', ts) AS day, SUM(steps) AS daily_steps
|
|
32
|
-
FROM
|
|
32
|
+
FROM v_activity
|
|
33
33
|
WHERE user_id = $userId AND brand = $brand AND family_id = $familyId
|
|
34
34
|
AND ts >= now() - INTERVAL (${windowDays + offsetDays}) DAY
|
|
35
35
|
AND ts < now() - INTERVAL (${offsetDays}) DAY
|
|
@@ -63,6 +63,10 @@ function fmt(v) {
|
|
|
63
63
|
return v == null ? 'n/a' : v.toFixed(1);
|
|
64
64
|
}
|
|
65
65
|
function finalize(text, rowCount) {
|
|
66
|
+
// principle 37 — every formatter's return path is guarded, not
|
|
67
|
+
// just the SpO2 one. Tool text lands in an LLM's context, and the
|
|
68
|
+
// model repeats whatever register it finds there.
|
|
69
|
+
assertNoBanTerms(text, 'compareTrend');
|
|
66
70
|
return { text, rowCount, bytes: formatBytes(text) };
|
|
67
71
|
}
|
|
68
72
|
//# sourceMappingURL=compare.js.map
|
|
@@ -1 +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;
|
|
1
|
+
{"version":3,"file":"compare.js","sourceRoot":"","sources":["../../../src/tools/impls/compare.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,11 +4,11 @@ export declare const getHRVInputSchema: z.ZodObject<{
|
|
|
4
4
|
userId: z.ZodString;
|
|
5
5
|
days: z.ZodNumber;
|
|
6
6
|
}, "strip", z.ZodTypeAny, {
|
|
7
|
-
days: number;
|
|
8
7
|
userId: string;
|
|
9
|
-
}, {
|
|
10
8
|
days: number;
|
|
9
|
+
}, {
|
|
11
10
|
userId: string;
|
|
11
|
+
days: number;
|
|
12
12
|
}>;
|
|
13
13
|
export type GetHRVInput = z.infer<typeof getHRVInputSchema>;
|
|
14
14
|
export declare const getHRV: ToolImpl<GetHRVInput>;
|
package/dist/tools/impls/hrv.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import { deltaPct, formatBytes } from '../formatters';
|
|
2
|
+
import { assertNoBanTerms, deltaPct, formatBytes } from '../formatters';
|
|
3
3
|
export const getHRVInputSchema = z.object({
|
|
4
4
|
userId: z.string(),
|
|
5
5
|
days: z.number().int().min(1).max(90),
|
|
6
6
|
});
|
|
7
7
|
export const getHRV = async (input, ctx) => {
|
|
8
8
|
const rows = await ctx.analytics.execute(`SELECT date_trunc('day', ts)::VARCHAR AS day, AVG(hrv_ms)::DOUBLE AS avg_hrv
|
|
9
|
-
FROM
|
|
9
|
+
FROM v_hrv
|
|
10
10
|
WHERE user_id = $userId AND brand = $brand AND family_id = $familyId
|
|
11
11
|
AND ts >= now() - INTERVAL ($days) DAY
|
|
12
12
|
AND hrv_ms IS NOT NULL
|
|
@@ -31,6 +31,10 @@ export const getHRV = async (input, ctx) => {
|
|
|
31
31
|
return finalize(text, rows.length);
|
|
32
32
|
};
|
|
33
33
|
function finalize(text, rowCount) {
|
|
34
|
+
// principle 37 — every formatter's return path is guarded, not
|
|
35
|
+
// just the SpO2 one. Tool text lands in an LLM's context, and the
|
|
36
|
+
// model repeats whatever register it finds there.
|
|
37
|
+
assertNoBanTerms(text, 'getHRV');
|
|
34
38
|
return { text, rowCount, bytes: formatBytes(text) };
|
|
35
39
|
}
|
|
36
40
|
//# sourceMappingURL=hrv.js.map
|
|
@@ -1 +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;
|
|
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,gBAAgB,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAGvE,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,+DAA+D;IAC/D,kEAAkE;IAClE,kDAAkD;IAClD,gBAAgB,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAA;IAChC,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,CAAC,IAAI,CAAC,EAAE,CAAA;AACrD,CAAC"}
|
|
@@ -3,15 +3,15 @@ import type { ToolImpl } from '../types';
|
|
|
3
3
|
export declare const getInsightsInputSchema: z.ZodObject<{
|
|
4
4
|
userId: z.ZodString;
|
|
5
5
|
days: z.ZodDefault<z.ZodNumber>;
|
|
6
|
-
severity: z.ZodOptional<z.ZodEnum<["info", "warn", "
|
|
6
|
+
severity: z.ZodOptional<z.ZodEnum<["info", "warn", "urgent"]>>;
|
|
7
7
|
}, "strip", z.ZodTypeAny, {
|
|
8
|
-
days: number;
|
|
9
8
|
userId: string;
|
|
10
|
-
|
|
9
|
+
days: number;
|
|
10
|
+
severity?: "info" | "warn" | "urgent" | undefined;
|
|
11
11
|
}, {
|
|
12
12
|
userId: string;
|
|
13
13
|
days?: number | undefined;
|
|
14
|
-
severity?: "info" | "warn" | "
|
|
14
|
+
severity?: "info" | "warn" | "urgent" | undefined;
|
|
15
15
|
}>;
|
|
16
16
|
export type GetInsightsInput = z.infer<typeof getInsightsInputSchema>;
|
|
17
17
|
export declare const getInsights: ToolImpl<GetInsightsInput>;
|
|
@@ -1 +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,
|
|
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,CA0ClD,CAAA"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import { formatBytes } from '../formatters';
|
|
2
|
+
import { assertNoBanTerms, formatBytes } from '../formatters';
|
|
3
3
|
export const getInsightsInputSchema = z.object({
|
|
4
4
|
userId: z.string(),
|
|
5
5
|
days: z.number().int().min(1).max(30).default(7),
|
|
6
|
-
severity: z.enum(['info', 'warn', '
|
|
6
|
+
severity: z.enum(['info', 'warn', 'urgent']).optional(),
|
|
7
7
|
});
|
|
8
8
|
export const getInsights = async (input, ctx) => {
|
|
9
9
|
const params = {
|
|
@@ -17,9 +17,12 @@ export const getInsights = async (input, ctx) => {
|
|
|
17
17
|
severityClause = ' AND severity = $severity';
|
|
18
18
|
params.severity = input.severity;
|
|
19
19
|
}
|
|
20
|
-
|
|
20
|
+
// dismissed_at IS NULL — dismissed insights are filtered from queries by
|
|
21
|
+
// default (principle 51); rows are preserved for restore/audit only.
|
|
22
|
+
const rows = await ctx.analytics.execute(`SELECT insight_id, ts::VARCHAR AS ts, severity, title, body
|
|
21
23
|
FROM insight
|
|
22
24
|
WHERE user_id = $userId AND brand = $brand AND family_id = $familyId
|
|
25
|
+
AND dismissed_at IS NULL
|
|
23
26
|
AND ts >= now() - INTERVAL ($days) DAY${severityClause}
|
|
24
27
|
ORDER BY ts DESC`, params);
|
|
25
28
|
if (rows.length === 0) {
|
|
@@ -36,6 +39,10 @@ export const getInsights = async (input, ctx) => {
|
|
|
36
39
|
return finalize(text, rows.length);
|
|
37
40
|
};
|
|
38
41
|
function finalize(text, rowCount) {
|
|
42
|
+
// principle 37 — every formatter's return path is guarded, not
|
|
43
|
+
// just the SpO2 one. Tool text lands in an LLM's context, and the
|
|
44
|
+
// model repeats whatever register it finds there.
|
|
45
|
+
assertNoBanTerms(text, 'getInsights');
|
|
39
46
|
return { text, rowCount, bytes: formatBytes(text) };
|
|
40
47
|
}
|
|
41
48
|
//# sourceMappingURL=insights.js.map
|
|
@@ -1 +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;
|
|
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,gBAAgB,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAG7D,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,QAAQ,CAAC,CAAC,CAAC,QAAQ,EAAE;CACxD,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,yEAAyE;IACzE,qEAAqE;IACrE,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,SAAS,CAAC,OAAO,CACtC;;;;+CAI2C,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,+DAA+D;IAC/D,kEAAkE;IAClE,kDAAkD;IAClD,gBAAgB,CAAC,IAAI,EAAE,aAAa,CAAC,CAAA;IACrC,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,CAAC,IAAI,CAAC,EAAE,CAAA;AACrD,CAAC"}
|
|
@@ -4,11 +4,11 @@ export declare const getSleepSummaryInputSchema: z.ZodObject<{
|
|
|
4
4
|
userId: z.ZodString;
|
|
5
5
|
days: z.ZodNumber;
|
|
6
6
|
}, "strip", z.ZodTypeAny, {
|
|
7
|
-
days: number;
|
|
8
7
|
userId: string;
|
|
9
|
-
}, {
|
|
10
8
|
days: number;
|
|
9
|
+
}, {
|
|
11
10
|
userId: string;
|
|
11
|
+
days: number;
|
|
12
12
|
}>;
|
|
13
13
|
export type GetSleepSummaryInput = z.infer<typeof getSleepSummaryInputSchema>;
|
|
14
14
|
export declare const getSleepSummary: ToolImpl<GetSleepSummaryInput>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import { formatBytes } from '../formatters';
|
|
2
|
+
import { assertNoBanTerms, formatBytes } from '../formatters';
|
|
3
3
|
export const getSleepSummaryInputSchema = z.object({
|
|
4
4
|
userId: z.string(),
|
|
5
5
|
days: z.number().int().min(1).max(90),
|
|
@@ -10,7 +10,7 @@ export const getSleepSummary = async (input, ctx) => {
|
|
|
10
10
|
SUM(deep_minutes)::INTEGER AS deep_minutes,
|
|
11
11
|
SUM(rem_minutes)::INTEGER AS rem_minutes,
|
|
12
12
|
SUM(light_minutes)::INTEGER AS light_minutes
|
|
13
|
-
FROM
|
|
13
|
+
FROM v_sleep_session
|
|
14
14
|
WHERE user_id = $userId AND brand = $brand AND family_id = $familyId
|
|
15
15
|
AND ts_start >= now() - INTERVAL ($days) DAY
|
|
16
16
|
GROUP BY night_of ORDER BY night_of`, {
|
|
@@ -38,6 +38,10 @@ export const getSleepSummary = async (input, ctx) => {
|
|
|
38
38
|
return finalize(text, rows.length);
|
|
39
39
|
};
|
|
40
40
|
function finalize(text, rowCount) {
|
|
41
|
+
// principle 37 — every formatter's return path is guarded, not
|
|
42
|
+
// just the SpO2 one. Tool text lands in an LLM's context, and the
|
|
43
|
+
// model repeats whatever register it finds there.
|
|
44
|
+
assertNoBanTerms(text, 'getSleepSummary');
|
|
41
45
|
return { text, rowCount, bytes: formatBytes(text) };
|
|
42
46
|
}
|
|
43
47
|
//# sourceMappingURL=sleep.js.map
|
|
@@ -1 +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;
|
|
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,gBAAgB,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAG7D,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,+DAA+D;IAC/D,kEAAkE;IAClE,kDAAkD;IAClD,gBAAgB,CAAC,IAAI,EAAE,iBAAiB,CAAC,CAAA;IACzC,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,CAAC,IAAI,CAAC,EAAE,CAAA;AACrD,CAAC"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `getSpO2` (Sprint 5 §5 / T-30 / T-31).
|
|
3
|
+
*
|
|
4
|
+
* Nightly SpO₂ summary for the conversational surface. Two things make it
|
|
5
|
+
* different from the other tools:
|
|
6
|
+
*
|
|
7
|
+
* 1. It is **sleep-scoped**. "How was my oxygen last night?" means during
|
|
8
|
+
* sleep, so the query INNER JOINs `v_sleep_session` rather than
|
|
9
|
+
* averaging around the clock — daytime readings would wash out exactly
|
|
10
|
+
* the dips the question is about.
|
|
11
|
+
* 2. Its formatter is the reference implementation for principle 37. The
|
|
12
|
+
* data has a field literally counting low moments, and the obvious
|
|
13
|
+
* clinical word for it is banned. See `formatters/spo2.ts`.
|
|
14
|
+
*/
|
|
15
|
+
import { z } from 'zod';
|
|
16
|
+
import type { ToolImpl } from '../types';
|
|
17
|
+
export declare const getSpO2InputSchema: z.ZodObject<{
|
|
18
|
+
userId: z.ZodString;
|
|
19
|
+
days: z.ZodNumber;
|
|
20
|
+
}, "strip", z.ZodTypeAny, {
|
|
21
|
+
userId: string;
|
|
22
|
+
days: number;
|
|
23
|
+
}, {
|
|
24
|
+
userId: string;
|
|
25
|
+
days: number;
|
|
26
|
+
}>;
|
|
27
|
+
export type GetSpO2Input = z.infer<typeof getSpO2InputSchema>;
|
|
28
|
+
/** One night's aggregate, as returned by the query. */
|
|
29
|
+
export interface SpO2NightRow {
|
|
30
|
+
night_of: string;
|
|
31
|
+
avg_spo2: number;
|
|
32
|
+
min_spo2: number;
|
|
33
|
+
low_moment_count: number;
|
|
34
|
+
}
|
|
35
|
+
/** Reference range from `user_baseline`, when mature. */
|
|
36
|
+
export interface SpO2BaselineRow {
|
|
37
|
+
p05: number;
|
|
38
|
+
p10: number;
|
|
39
|
+
p50: number;
|
|
40
|
+
computed_at: string;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Threshold below which a reading counts as a "low moment".
|
|
44
|
+
*
|
|
45
|
+
* Fixed at 90 rather than read from `user:spo2SafeLevel`: this is a
|
|
46
|
+
* descriptive summary, not the user's alert. Tying it to their alert
|
|
47
|
+
* threshold would make the same night read differently after they moved a
|
|
48
|
+
* slider, which is confusing in a narrative context.
|
|
49
|
+
*/
|
|
50
|
+
export declare const LOW_MOMENT_THRESHOLD = 90;
|
|
51
|
+
export declare const getSpO2: ToolImpl<GetSpO2Input>;
|
|
52
|
+
//# sourceMappingURL=spo2.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spo2.d.ts","sourceRoot":"","sources":["../../../src/tools/impls/spo2.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAGvB,OAAO,KAAK,EAAE,QAAQ,EAAc,MAAM,UAAU,CAAA;AAEpD,eAAO,MAAM,kBAAkB;;;;;;;;;EAG7B,CAAA;AAEF,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAA;AAE7D,uDAAuD;AACvD,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,gBAAgB,EAAE,MAAM,CAAA;CACzB;AAED,yDAAyD;AACzD,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IACX,WAAW,EAAE,MAAM,CAAA;CACpB;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,KAAK,CAAA;AAEtC,eAAO,MAAM,OAAO,EAAE,QAAQ,CAAC,YAAY,CAuC1C,CAAA"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `getSpO2` (Sprint 5 §5 / T-30 / T-31).
|
|
3
|
+
*
|
|
4
|
+
* Nightly SpO₂ summary for the conversational surface. Two things make it
|
|
5
|
+
* different from the other tools:
|
|
6
|
+
*
|
|
7
|
+
* 1. It is **sleep-scoped**. "How was my oxygen last night?" means during
|
|
8
|
+
* sleep, so the query INNER JOINs `v_sleep_session` rather than
|
|
9
|
+
* averaging around the clock — daytime readings would wash out exactly
|
|
10
|
+
* the dips the question is about.
|
|
11
|
+
* 2. Its formatter is the reference implementation for principle 37. The
|
|
12
|
+
* data has a field literally counting low moments, and the obvious
|
|
13
|
+
* clinical word for it is banned. See `formatters/spo2.ts`.
|
|
14
|
+
*/
|
|
15
|
+
import { z } from 'zod';
|
|
16
|
+
import { formatSpO2 } from '../formatters/spo2';
|
|
17
|
+
import { formatBytes } from '../formatters';
|
|
18
|
+
export const getSpO2InputSchema = z.object({
|
|
19
|
+
userId: z.string(),
|
|
20
|
+
days: z.number().int().min(1).max(90),
|
|
21
|
+
});
|
|
22
|
+
/**
|
|
23
|
+
* Threshold below which a reading counts as a "low moment".
|
|
24
|
+
*
|
|
25
|
+
* Fixed at 90 rather than read from `user:spo2SafeLevel`: this is a
|
|
26
|
+
* descriptive summary, not the user's alert. Tying it to their alert
|
|
27
|
+
* threshold would make the same night read differently after they moved a
|
|
28
|
+
* slider, which is confusing in a narrative context.
|
|
29
|
+
*/
|
|
30
|
+
export const LOW_MOMENT_THRESHOLD = 90;
|
|
31
|
+
export const getSpO2 = async (input, ctx) => {
|
|
32
|
+
const params = {
|
|
33
|
+
userId: input.userId,
|
|
34
|
+
brand: ctx.brand,
|
|
35
|
+
familyId: ctx.familyId,
|
|
36
|
+
days: input.days,
|
|
37
|
+
};
|
|
38
|
+
const nights = await ctx.analytics.execute(`SELECT s.night_of::VARCHAR AS night_of,
|
|
39
|
+
AVG(m.spo2)::DOUBLE AS avg_spo2,
|
|
40
|
+
MIN(m.spo2)::DOUBLE AS min_spo2,
|
|
41
|
+
COUNT(*) FILTER (WHERE m.spo2 < ${LOW_MOMENT_THRESHOLD})::INTEGER AS low_moment_count
|
|
42
|
+
FROM v_spo2 m
|
|
43
|
+
INNER JOIN v_sleep_session s
|
|
44
|
+
ON s.user_id = m.user_id
|
|
45
|
+
AND s.brand = m.brand
|
|
46
|
+
AND s.family_id = m.family_id
|
|
47
|
+
AND m.ts BETWEEN s.ts_start AND s.ts_end
|
|
48
|
+
WHERE m.user_id = $userId AND m.brand = $brand AND m.family_id = $familyId
|
|
49
|
+
AND m.ts >= now() - INTERVAL ($days) DAY
|
|
50
|
+
GROUP BY s.night_of
|
|
51
|
+
ORDER BY s.night_of`, params);
|
|
52
|
+
// Reference range comes from the shared baseline table so the narrative
|
|
53
|
+
// and the chart's "usual range" band cannot disagree.
|
|
54
|
+
const baselineRows = await ctx.analytics.execute(`SELECT p05, p10, p50, computed_at::VARCHAR AS computed_at
|
|
55
|
+
FROM user_baseline
|
|
56
|
+
WHERE user_id = $userId AND brand = $brand AND family_id = $familyId
|
|
57
|
+
AND metric = 'spo2' AND window_days = 30
|
|
58
|
+
LIMIT 1`, { userId: input.userId, brand: ctx.brand, familyId: ctx.familyId });
|
|
59
|
+
const text = formatSpO2(nights, baselineRows[0] ?? null, input.days);
|
|
60
|
+
return finalize(text, nights.length);
|
|
61
|
+
};
|
|
62
|
+
function finalize(text, rowCount) {
|
|
63
|
+
return { text, rowCount, bytes: formatBytes(text) };
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=spo2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spo2.js","sourceRoot":"","sources":["../../../src/tools/impls/spo2.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAA;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAG3C,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC;IACzC,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;AAoBF;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,EAAE,CAAA;AAEtC,MAAM,CAAC,MAAM,OAAO,GAA2B,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;IAClE,MAAM,MAAM,GAAG;QACb,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;IAED,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,SAAS,CAAC,OAAO,CACxC;;;8CAG0C,oBAAoB;;;;;;;;;;yBAUzC,EACrB,MAAM,CACP,CAAA;IAED,wEAAwE;IACxE,sDAAsD;IACtD,MAAM,YAAY,GAAG,MAAM,GAAG,CAAC,SAAS,CAAC,OAAO,CAC9C;;;;aAIS,EACT,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,CAAC,QAAQ,EAAE,CACnE,CAAA;IAED,MAAM,IAAI,GAAG,UAAU,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAA;IACpE,OAAO,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,CAAA;AACtC,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
|
@@ -11,4 +11,5 @@ export { getActivityTotal, getActivityTotalInputSchema, type GetActivityTotalInp
|
|
|
11
11
|
export { compareTrend, compareTrendInputSchema, type CompareTrendInput, } from './impls/compare';
|
|
12
12
|
export { detectAnomaly, detectAnomalyInputSchema, type DetectAnomalyInput, } from './impls/anomaly';
|
|
13
13
|
export { getInsights, getInsightsInputSchema, type GetInsightsInput, } from './impls/insights';
|
|
14
|
+
export { applyPreferredLanguage, assertNoBanTerms, BANNED_MEDICAL_VOCABULARY, type BannedTerm, findBanTerms, FormatterCopyError, PREFERRED_LANGUAGE, } from './formatters';
|
|
14
15
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
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"}
|
|
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;AAKzB,OAAO,EACL,sBAAsB,EACtB,gBAAgB,EAChB,yBAAyB,EACzB,KAAK,UAAU,EACf,YAAY,EACZ,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,cAAc,CAAA"}
|
package/dist/tools/index.js
CHANGED
|
@@ -19,4 +19,8 @@ export { getActivityTotal, getActivityTotalInputSchema, } from './impls/activity
|
|
|
19
19
|
export { compareTrend, compareTrendInputSchema, } from './impls/compare';
|
|
20
20
|
export { detectAnomaly, detectAnomalyInputSchema, } from './impls/anomaly';
|
|
21
21
|
export { getInsights, getInsightsInputSchema, } from './impls/insights';
|
|
22
|
+
// Copy guardrails (Sprint 5 T-29, principle 37). Exported so app-side
|
|
23
|
+
// formatters and registry `transform` functions can apply the same
|
|
24
|
+
// contract to text they generate.
|
|
25
|
+
export { applyPreferredLanguage, assertNoBanTerms, BANNED_MEDICAL_VOCABULARY, findBanTerms, FormatterCopyError, PREFERRED_LANGUAGE, } from './formatters';
|
|
22
26
|
//# 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":"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"}
|
|
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;AAEzB,sEAAsE;AACtE,mEAAmE;AACnE,kCAAkC;AAClC,OAAO,EACL,sBAAsB,EACtB,gBAAgB,EAChB,yBAAyB,EAEzB,YAAY,EACZ,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,cAAc,CAAA"}
|
|
@@ -1,17 +1,34 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* MCP gating — advisory flag + enforced production guard.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* (`__DEV__ === true`)
|
|
6
|
-
* `ENABLE_MCP_SERVER=1` set. Prod RN builds
|
|
7
|
-
* and no `process.env`, so the check returns
|
|
8
|
-
* short-circuits — with `sideEffects: false` at
|
|
9
|
-
* this lets bundlers drop every module reachable only
|
|
10
|
-
* MCP subpath (including `@modelcontextprotocol/sdk`).
|
|
4
|
+
* `shouldStartMcpServer()` is the *advisory* cross-runtime dev flag:
|
|
5
|
+
* `true` iff the process is a React Native dev build (`__DEV__ === true`)
|
|
6
|
+
* **or** the Node process has `ENABLE_MCP_SERVER=1` set. Prod RN builds
|
|
7
|
+
* have `__DEV__ === false` and no `process.env`, so the check returns
|
|
8
|
+
* `false` and the caller short-circuits — with `sideEffects: false` at
|
|
9
|
+
* the package root, this lets bundlers drop every module reachable only
|
|
10
|
+
* through the MCP subpath (including `@modelcontextprotocol/sdk`).
|
|
11
11
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* `
|
|
12
|
+
* `assertMcpAllowed()` is the *enforced* production runtime guard
|
|
13
|
+
* (principle 41): it THROWS `McpDisabledError` unless `__DEV__` is truthy
|
|
14
|
+
* **and** `ENABLE_MCP_SERVER=1` is set — dev signal AND explicit opt-in,
|
|
15
|
+
* not either/or. Every entry point that can start a transport
|
|
16
|
+
* (`createMcpServer`, `createHttpTransport`) calls it; the advisory flag
|
|
17
|
+
* alone never gates anything at runtime.
|
|
18
|
+
*
|
|
19
|
+
* Both helpers are intentionally pure — importing this module does not
|
|
20
|
+
* itself import the SDK. Callers should still gate SDK imports behind
|
|
21
|
+
* `shouldStartMcpServer()` at their entry point for tree-shaking.
|
|
15
22
|
*/
|
|
16
23
|
export declare function shouldStartMcpServer(): boolean;
|
|
24
|
+
/** Thrown by `assertMcpAllowed()` when the MCP surface is disabled. */
|
|
25
|
+
export declare class McpDisabledError extends Error {
|
|
26
|
+
constructor();
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Enforced production guard (principle 41): throw unless
|
|
30
|
+
* `__DEV__ && ENABLE_MCP_SERVER`. Called by every transport-capable
|
|
31
|
+
* entry point in this subpath.
|
|
32
|
+
*/
|
|
33
|
+
export declare function assertMcpAllowed(): void;
|
|
17
34
|
//# sourceMappingURL=guard.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"guard.d.ts","sourceRoot":"","sources":["../../../src/tools/mcp/guard.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"guard.d.ts","sourceRoot":"","sources":["../../../src/tools/mcp/guard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAaH,wBAAgB,oBAAoB,IAAI,OAAO,CAE9C;AAED,uEAAuE;AACvE,qBAAa,gBAAiB,SAAQ,KAAK;;CAS1C;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,IAAI,IAAI,CAIvC"}
|
package/dist/tools/mcp/guard.js
CHANGED
|
@@ -1,22 +1,52 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* MCP gating — advisory flag + enforced production guard.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* (`__DEV__ === true`)
|
|
6
|
-
* `ENABLE_MCP_SERVER=1` set. Prod RN builds
|
|
7
|
-
* and no `process.env`, so the check returns
|
|
8
|
-
* short-circuits — with `sideEffects: false` at
|
|
9
|
-
* this lets bundlers drop every module reachable only
|
|
10
|
-
* MCP subpath (including `@modelcontextprotocol/sdk`).
|
|
4
|
+
* `shouldStartMcpServer()` is the *advisory* cross-runtime dev flag:
|
|
5
|
+
* `true` iff the process is a React Native dev build (`__DEV__ === true`)
|
|
6
|
+
* **or** the Node process has `ENABLE_MCP_SERVER=1` set. Prod RN builds
|
|
7
|
+
* have `__DEV__ === false` and no `process.env`, so the check returns
|
|
8
|
+
* `false` and the caller short-circuits — with `sideEffects: false` at
|
|
9
|
+
* the package root, this lets bundlers drop every module reachable only
|
|
10
|
+
* through the MCP subpath (including `@modelcontextprotocol/sdk`).
|
|
11
11
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* `
|
|
12
|
+
* `assertMcpAllowed()` is the *enforced* production runtime guard
|
|
13
|
+
* (principle 41): it THROWS `McpDisabledError` unless `__DEV__` is truthy
|
|
14
|
+
* **and** `ENABLE_MCP_SERVER=1` is set — dev signal AND explicit opt-in,
|
|
15
|
+
* not either/or. Every entry point that can start a transport
|
|
16
|
+
* (`createMcpServer`, `createHttpTransport`) calls it; the advisory flag
|
|
17
|
+
* alone never gates anything at runtime.
|
|
18
|
+
*
|
|
19
|
+
* Both helpers are intentionally pure — importing this module does not
|
|
20
|
+
* itself import the SDK. Callers should still gate SDK imports behind
|
|
21
|
+
* `shouldStartMcpServer()` at their entry point for tree-shaking.
|
|
15
22
|
*/
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
23
|
+
function isRnDev() {
|
|
24
|
+
return typeof __DEV__ !== 'undefined' && __DEV__ === true;
|
|
25
|
+
}
|
|
26
|
+
function isMcpFlagSet() {
|
|
27
|
+
return typeof process !== 'undefined'
|
|
19
28
|
&& process.env?.ENABLE_MCP_SERVER === '1';
|
|
20
|
-
|
|
29
|
+
}
|
|
30
|
+
export function shouldStartMcpServer() {
|
|
31
|
+
return isRnDev() || isMcpFlagSet();
|
|
32
|
+
}
|
|
33
|
+
/** Thrown by `assertMcpAllowed()` when the MCP surface is disabled. */
|
|
34
|
+
export class McpDisabledError extends Error {
|
|
35
|
+
constructor() {
|
|
36
|
+
super('MCP server is disabled: requires a dev build (__DEV__ === true) '
|
|
37
|
+
+ 'AND ENABLE_MCP_SERVER=1 (principle 41 — MCP is dev-only). '
|
|
38
|
+
+ 'Refusing to start a transport in a production runtime.');
|
|
39
|
+
this.name = 'McpDisabledError';
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Enforced production guard (principle 41): throw unless
|
|
44
|
+
* `__DEV__ && ENABLE_MCP_SERVER`. Called by every transport-capable
|
|
45
|
+
* entry point in this subpath.
|
|
46
|
+
*/
|
|
47
|
+
export function assertMcpAllowed() {
|
|
48
|
+
if (!(isRnDev() && isMcpFlagSet())) {
|
|
49
|
+
throw new McpDisabledError();
|
|
50
|
+
}
|
|
21
51
|
}
|
|
22
52
|
//# sourceMappingURL=guard.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"guard.js","sourceRoot":"","sources":["../../../src/tools/mcp/guard.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"guard.js","sourceRoot":"","sources":["../../../src/tools/mcp/guard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAIH,SAAS,OAAO;IACd,OAAO,OAAO,OAAO,KAAK,WAAW,IAAI,OAAO,KAAK,IAAI,CAAA;AAC3D,CAAC;AAED,SAAS,YAAY;IACnB,OAAO,OAAO,OAAO,KAAK,WAAW;WAChC,OAAO,CAAC,GAAG,EAAE,iBAAiB,KAAK,GAAG,CAAA;AAC7C,CAAC;AAED,MAAM,UAAU,oBAAoB;IAClC,OAAO,OAAO,EAAE,IAAI,YAAY,EAAE,CAAA;AACpC,CAAC;AAED,uEAAuE;AACvE,MAAM,OAAO,gBAAiB,SAAQ,KAAK;IACzC;QACE,KAAK,CACH,kEAAkE;cAChE,4DAA4D;cAC5D,wDAAwD,CAC3D,CAAA;QACD,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAA;IAChC,CAAC;CACF;AAED;;;;GAIG;AACH,MAAM,UAAU,gBAAgB;IAC9B,IAAI,CAAC,CAAC,OAAO,EAAE,IAAI,YAAY,EAAE,CAAC,EAAE,CAAC;QACnC,MAAM,IAAI,gBAAgB,EAAE,CAAA;IAC9B,CAAC;AACH,CAAC"}
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
export { toMcpTools } from './adapter';
|
|
12
12
|
export type { McpTool } from './adapter';
|
|
13
|
-
export { shouldStartMcpServer } from './guard';
|
|
13
|
+
export { assertMcpAllowed, McpDisabledError, shouldStartMcpServer } from './guard';
|
|
14
14
|
export { createMcpServer } from './server';
|
|
15
15
|
export type { CreateMcpServerConfig, McpServerHandle, } from './server';
|
|
16
16
|
export { createStdioTransport } from './transports/stdio';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/tools/mcp/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AACtC,YAAY,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAExC,OAAO,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/tools/mcp/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AACtC,YAAY,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAExC,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAA;AAElF,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAC1C,YAAY,EACV,qBAAqB,EACrB,eAAe,GAChB,MAAM,UAAU,CAAA;AAEjB,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAA;AACzD,YAAY,EAAE,2BAA2B,EAAE,MAAM,oBAAoB,CAAA;AAErE,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAA;AACvD,YAAY,EACV,yBAAyB,EACzB,mBAAmB,GACpB,MAAM,mBAAmB,CAAA"}
|