@mongrov/analytics 0.1.0-alpha.8 → 0.1.0-beta.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 +47 -11
- package/dist/core/factory.d.ts.map +1 -1
- package/dist/core/factory.js +26 -0
- package/dist/core/factory.js.map +1 -1
- package/dist/core/schemas.js +18 -18
- package/dist/core/warehouse.d.ts.map +1 -1
- package/dist/core/warehouse.js +13 -1
- package/dist/core/warehouse.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/mcp/adapter.d.ts +33 -0
- package/dist/tools/mcp/adapter.d.ts.map +1 -0
- package/dist/tools/mcp/adapter.js +56 -0
- package/dist/tools/mcp/adapter.js.map +1 -0
- package/dist/tools/mcp/guard.d.ts +17 -0
- package/dist/tools/mcp/guard.d.ts.map +1 -0
- package/dist/tools/mcp/guard.js +22 -0
- package/dist/tools/mcp/guard.js.map +1 -0
- package/dist/tools/mcp/index.d.ts +20 -0
- package/dist/tools/mcp/index.d.ts.map +1 -0
- package/dist/tools/mcp/index.js +16 -0
- package/dist/tools/mcp/index.js.map +1 -0
- package/dist/tools/mcp/server.d.ts +39 -0
- package/dist/tools/mcp/server.d.ts.map +1 -0
- package/dist/tools/mcp/server.js +70 -0
- package/dist/tools/mcp/server.js.map +1 -0
- package/dist/tools/mcp/transports/http.d.ts +54 -0
- package/dist/tools/mcp/transports/http.d.ts.map +1 -0
- package/dist/tools/mcp/transports/http.js +133 -0
- package/dist/tools/mcp/transports/http.js.map +1 -0
- package/dist/tools/mcp/transports/stdio.d.ts +22 -0
- package/dist/tools/mcp/transports/stdio.d.ts.map +1 -0
- package/dist/tools/mcp/transports/stdio.js +19 -0
- package/dist/tools/mcp/transports/stdio.js.map +1 -0
- 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 +21 -4
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authorize hooks for analytics tools.
|
|
3
|
+
*
|
|
4
|
+
* `familyScopeAuthorize` — grants access iff `args.userId` is either
|
|
5
|
+
* the requester themselves or another member of the requester's
|
|
6
|
+
* family. When `config.familyMembersProvider` is supplied (the
|
|
7
|
+
* factory path since T-11), membership resolves via that provider;
|
|
8
|
+
* otherwise the legacy SQL path against a `family_member` table is
|
|
9
|
+
* used (kept for standalone hook usage and backwards compat).
|
|
10
|
+
*
|
|
11
|
+
* Both hooks fail closed on any error — provider throws, engine
|
|
12
|
+
* throws, missing/malformed args — return `false` rather than
|
|
13
|
+
* surfacing the error to the LLM.
|
|
14
|
+
*/
|
|
15
|
+
export function familyScopeAuthorize(analytics, config = {}) {
|
|
16
|
+
return async (_toolName, args, ctx) => {
|
|
17
|
+
const targetUserId = args.userId;
|
|
18
|
+
if (typeof targetUserId !== 'string' || targetUserId.length === 0) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
if (targetUserId === ctx.requesterUserId) {
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
if (config.familyMembersProvider) {
|
|
25
|
+
try {
|
|
26
|
+
const members = await config.familyMembersProvider({
|
|
27
|
+
brand: ctx.brand,
|
|
28
|
+
familyId: ctx.familyId,
|
|
29
|
+
});
|
|
30
|
+
return members.includes(targetUserId);
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
try {
|
|
37
|
+
const rows = await analytics.execute('SELECT 1 AS one FROM family_member '
|
|
38
|
+
+ 'WHERE family_id = $familyId AND user_id = $userId LIMIT 1', { familyId: ctx.familyId, userId: targetUserId });
|
|
39
|
+
return rows.length > 0;
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* `orgScopeAuthorize` — parity with family scope but against an
|
|
48
|
+
* `org_member` table. Orgs are not first-class in v0.1.0; the ctx
|
|
49
|
+
* `familyId` is treated as the org id until org membership is wired
|
|
50
|
+
* through `RequestContext`.
|
|
51
|
+
*
|
|
52
|
+
* When `familyMembersProvider` is supplied, the same provider is
|
|
53
|
+
* consulted (v0.1.0 shim — a dedicated `orgMembersProvider` will
|
|
54
|
+
* split out once orgs land).
|
|
55
|
+
*/
|
|
56
|
+
export function orgScopeAuthorize(analytics, config = {}) {
|
|
57
|
+
return async (_toolName, args, ctx) => {
|
|
58
|
+
const targetUserId = args.userId;
|
|
59
|
+
if (typeof targetUserId !== 'string' || targetUserId.length === 0) {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
if (targetUserId === ctx.requesterUserId) {
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
if (config.familyMembersProvider) {
|
|
66
|
+
try {
|
|
67
|
+
const members = await config.familyMembersProvider({
|
|
68
|
+
brand: ctx.brand,
|
|
69
|
+
familyId: ctx.familyId,
|
|
70
|
+
});
|
|
71
|
+
return members.includes(targetUserId);
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
try {
|
|
78
|
+
const rows = await analytics.execute('SELECT 1 AS one FROM org_member '
|
|
79
|
+
+ 'WHERE org_id = $orgId AND user_id = $userId LIMIT 1', { orgId: ctx.familyId, userId: targetUserId });
|
|
80
|
+
return rows.length > 0;
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=authorize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authorize.js","sourceRoot":"","sources":["../../src/tools/authorize.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAaH,MAAM,UAAU,oBAAoB,CAClC,SAA0B,EAC1B,SAA0B,EAAE;IAE5B,OAAO,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE;QACpC,MAAM,YAAY,GAAG,IAAI,CAAC,MAAM,CAAA;QAChC,IAAI,OAAO,YAAY,KAAK,QAAQ,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAClE,OAAO,KAAK,CAAA;QACd,CAAC;QACD,IAAI,YAAY,KAAK,GAAG,CAAC,eAAe,EAAE,CAAC;YACzC,OAAO,IAAI,CAAA;QACb,CAAC;QAED,IAAI,MAAM,CAAC,qBAAqB,EAAE,CAAC;YACjC,IAAI,CAAC;gBACH,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,qBAAqB,CAAC;oBACjD,KAAK,EAAE,GAAG,CAAC,KAAK;oBAChB,QAAQ,EAAE,GAAG,CAAC,QAAQ;iBACvB,CAAC,CAAA;gBACF,OAAO,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAA;YACvC,CAAC;YACD,MAAM,CAAC;gBACL,OAAO,KAAK,CAAA;YACd,CAAC;QACH,CAAC;QAED,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,OAAO,CAClC,qCAAqC;kBACnC,2DAA2D,EAC7D,EAAE,QAAQ,EAAE,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,CACjD,CAAA;YACD,OAAO,IAAI,CAAC,MAAM,GAAG,CAAC,CAAA;QACxB,CAAC;QACD,MAAM,CAAC;YACL,OAAO,KAAK,CAAA;QACd,CAAC;IACH,CAAC,CAAA;AACH,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,iBAAiB,CAC/B,SAA0B,EAC1B,SAA0B,EAAE;IAE5B,OAAO,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE;QACpC,MAAM,YAAY,GAAG,IAAI,CAAC,MAAM,CAAA;QAChC,IAAI,OAAO,YAAY,KAAK,QAAQ,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAClE,OAAO,KAAK,CAAA;QACd,CAAC;QACD,IAAI,YAAY,KAAK,GAAG,CAAC,eAAe,EAAE,CAAC;YACzC,OAAO,IAAI,CAAA;QACb,CAAC;QAED,IAAI,MAAM,CAAC,qBAAqB,EAAE,CAAC;YACjC,IAAI,CAAC;gBACH,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,qBAAqB,CAAC;oBACjD,KAAK,EAAE,GAAG,CAAC,KAAK;oBAChB,QAAQ,EAAE,GAAG,CAAC,QAAQ;iBACvB,CAAC,CAAA;gBACF,OAAO,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAA;YACvC,CAAC;YACD,MAAM,CAAC;gBACL,OAAO,KAAK,CAAA;YACd,CAAC;QACH,CAAC;QAED,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,OAAO,CAClC,kCAAkC;kBAChC,qDAAqD,EACvD,EAAE,KAAK,EAAE,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,CAC9C,CAAA;YACD,OAAO,IAAI,CAAC,MAAM,GAAG,CAAC,CAAA;QACxB,CAAC;QACD,MAAM,CAAC;YACL,OAAO,KAAK,CAAA;QACd,CAAC;IACH,CAAC,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Output budget enforcement for tool results.
|
|
3
|
+
*
|
|
4
|
+
* `applyOutputBudget` truncates `ToolResult.text` when its UTF-8
|
|
5
|
+
* byte size exceeds `budget.maxBytes`, appending a `\n[truncated]`
|
|
6
|
+
* suffix. Truncation is UTF-8-safe: bytes are sliced and re-decoded
|
|
7
|
+
* with `fatal: false` so any partial multi-byte codepoint at the
|
|
8
|
+
* boundary is dropped rather than emitting invalid sequences.
|
|
9
|
+
*
|
|
10
|
+
* `budget.maxRows` is informational — tool impls limit row count
|
|
11
|
+
* via SQL LIMIT. This helper does not attempt row-level truncation.
|
|
12
|
+
*/
|
|
13
|
+
import type { OutputBudget, ToolResult } from './types';
|
|
14
|
+
export declare function applyOutputBudget(result: ToolResult, budget: OutputBudget): ToolResult;
|
|
15
|
+
//# sourceMappingURL=budget.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"budget.d.ts","sourceRoot":"","sources":["../../src/tools/budget.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAIvD,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,UAAU,EAClB,MAAM,EAAE,YAAY,GACnB,UAAU,CAiBZ"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Output budget enforcement for tool results.
|
|
3
|
+
*
|
|
4
|
+
* `applyOutputBudget` truncates `ToolResult.text` when its UTF-8
|
|
5
|
+
* byte size exceeds `budget.maxBytes`, appending a `\n[truncated]`
|
|
6
|
+
* suffix. Truncation is UTF-8-safe: bytes are sliced and re-decoded
|
|
7
|
+
* with `fatal: false` so any partial multi-byte codepoint at the
|
|
8
|
+
* boundary is dropped rather than emitting invalid sequences.
|
|
9
|
+
*
|
|
10
|
+
* `budget.maxRows` is informational — tool impls limit row count
|
|
11
|
+
* via SQL LIMIT. This helper does not attempt row-level truncation.
|
|
12
|
+
*/
|
|
13
|
+
const TRUNCATE_SUFFIX = '\n[truncated]';
|
|
14
|
+
export function applyOutputBudget(result, budget) {
|
|
15
|
+
if (result.bytes <= budget.maxBytes)
|
|
16
|
+
return result;
|
|
17
|
+
const encoder = new TextEncoder();
|
|
18
|
+
const suffixBytes = encoder.encode(TRUNCATE_SUFFIX).byteLength;
|
|
19
|
+
const targetBytes = Math.max(0, budget.maxBytes - suffixBytes);
|
|
20
|
+
const encoded = encoder.encode(result.text);
|
|
21
|
+
const safeEnd = utf8SafeBoundary(encoded, Math.min(targetBytes, encoded.byteLength));
|
|
22
|
+
const decoded = new TextDecoder('utf-8').decode(encoded.slice(0, safeEnd));
|
|
23
|
+
const truncated = `${decoded}${TRUNCATE_SUFFIX}`;
|
|
24
|
+
return {
|
|
25
|
+
text: truncated,
|
|
26
|
+
rowCount: result.rowCount,
|
|
27
|
+
bytes: encoder.encode(truncated).byteLength,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Walk `end` backward until it points to a valid UTF-8 codepoint
|
|
32
|
+
* boundary — i.e. either past-the-end of the array or at a byte
|
|
33
|
+
* whose top bits are not `10` (continuation). This guarantees that
|
|
34
|
+
* `bytes.slice(0, boundary)` decodes without emitting replacement
|
|
35
|
+
* characters for a partial trailing multibyte sequence.
|
|
36
|
+
*/
|
|
37
|
+
function utf8SafeBoundary(bytes, end) {
|
|
38
|
+
let boundary = end;
|
|
39
|
+
// A continuation byte matches 10xxxxxx (0x80..0xBF).
|
|
40
|
+
while (boundary > 0 && (bytes[boundary] & 0xC0) === 0x80) {
|
|
41
|
+
boundary -= 1;
|
|
42
|
+
}
|
|
43
|
+
// If boundary now points to a lead byte (top bit set), check
|
|
44
|
+
// whether the full sequence fits inside `end`. If not, drop the
|
|
45
|
+
// lead byte as well.
|
|
46
|
+
if (boundary > 0 && boundary < bytes.byteLength) {
|
|
47
|
+
const lead = bytes[boundary];
|
|
48
|
+
let seqLen = 0;
|
|
49
|
+
if ((lead & 0x80) === 0x00)
|
|
50
|
+
seqLen = 1;
|
|
51
|
+
else if ((lead & 0xE0) === 0xC0)
|
|
52
|
+
seqLen = 2;
|
|
53
|
+
else if ((lead & 0xF0) === 0xE0)
|
|
54
|
+
seqLen = 3;
|
|
55
|
+
else if ((lead & 0xF8) === 0xF0)
|
|
56
|
+
seqLen = 4;
|
|
57
|
+
if (seqLen > 1 && boundary + seqLen > end) {
|
|
58
|
+
// Partial sequence — drop lead byte.
|
|
59
|
+
return boundary;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return boundary;
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=budget.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"budget.js","sourceRoot":"","sources":["../../src/tools/budget.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,MAAM,eAAe,GAAG,eAAe,CAAA;AAEvC,MAAM,UAAU,iBAAiB,CAC/B,MAAkB,EAClB,MAAoB;IAEpB,IAAI,MAAM,CAAC,KAAK,IAAI,MAAM,CAAC,QAAQ;QAAE,OAAO,MAAM,CAAA;IAElD,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAA;IACjC,MAAM,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC,UAAU,CAAA;IAC9D,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,QAAQ,GAAG,WAAW,CAAC,CAAA;IAE9D,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;IAC3C,MAAM,OAAO,GAAG,gBAAgB,CAAC,OAAO,EAAE,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC,CAAA;IACpF,MAAM,OAAO,GAAG,IAAI,WAAW,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAA;IAC1E,MAAM,SAAS,GAAG,GAAG,OAAO,GAAG,eAAe,EAAE,CAAA;IAEhD,OAAO;QACL,IAAI,EAAE,SAAS;QACf,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,UAAU;KAC5C,CAAA;AACH,CAAC;AAED;;;;;;GAMG;AACH,SAAS,gBAAgB,CAAC,KAAiB,EAAE,GAAW;IACtD,IAAI,QAAQ,GAAG,GAAG,CAAA;IAClB,qDAAqD;IACrD,OAAO,QAAQ,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC;QACzD,QAAQ,IAAI,CAAC,CAAA;IACf,CAAC;IACD,6DAA6D;IAC7D,gEAAgE;IAChE,qBAAqB;IACrB,IAAI,QAAQ,GAAG,CAAC,IAAI,QAAQ,GAAG,KAAK,CAAC,UAAU,EAAE,CAAC;QAChD,MAAM,IAAI,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAA;QAC5B,IAAI,MAAM,GAAG,CAAC,CAAA;QACd,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,KAAK,IAAI;YAAE,MAAM,GAAG,CAAC,CAAA;aACjC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,KAAK,IAAI;YAAE,MAAM,GAAG,CAAC,CAAA;aACtC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,KAAK,IAAI;YAAE,MAAM,GAAG,CAAC,CAAA;aACtC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,KAAK,IAAI;YAAE,MAAM,GAAG,CAAC,CAAA;QAC3C,IAAI,MAAM,GAAG,CAAC,IAAI,QAAQ,GAAG,MAAM,GAAG,GAAG,EAAE,CAAC;YAC1C,qCAAqC;YACrC,OAAO,QAAQ,CAAA;QACjB,CAAC;IACH,CAAC;IACD,OAAO,QAAQ,CAAA;AACjB,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `createAnalyticsTools` — assemble the 6 wrapped AI SDK tools into
|
|
3
|
+
* a single map ready to hand to `streamText({ tools })` / `generateText`.
|
|
4
|
+
*
|
|
5
|
+
* Wires:
|
|
6
|
+
* - rate limiter (or `null` if `rateLimit: false`)
|
|
7
|
+
* - authorize hook (default `familyScopeAuthorize`, optionally with
|
|
8
|
+
* an injected `familyMembersProvider`)
|
|
9
|
+
* - audit writer (batched, optional)
|
|
10
|
+
* - output budget (default 4096 bytes / 100 rows)
|
|
11
|
+
*
|
|
12
|
+
* Per-request context threading: the AI SDK's `execute(input)` has no
|
|
13
|
+
* ctx arg. We hold a mutable `currentCtx` container and expose
|
|
14
|
+
* `setContext(ctx)`. Consumers (zivaone_app) call `setContext` before
|
|
15
|
+
* each LLM turn — matching how `@mongrov/ai`'s `AIProvider` threads
|
|
16
|
+
* per-request scopes. This design keeps rate-limiter buckets + audit
|
|
17
|
+
* writer state shared across requests rather than rebuilding the
|
|
18
|
+
* whole tool map per turn.
|
|
19
|
+
*/
|
|
20
|
+
import type { AnalyticsToolsConfig, AuditWriter, ToolContext } from './types';
|
|
21
|
+
import { makeTool } from './wrap';
|
|
22
|
+
import { type GetActivityTotalInput } from './impls/activity';
|
|
23
|
+
import { type DetectAnomalyInput } from './impls/anomaly';
|
|
24
|
+
import { type CompareTrendInput } from './impls/compare';
|
|
25
|
+
import { type GetHRVInput } from './impls/hrv';
|
|
26
|
+
import { type GetInsightsInput } from './impls/insights';
|
|
27
|
+
import { type GetSleepSummaryInput } from './impls/sleep';
|
|
28
|
+
export interface AnalyticsToolMap {
|
|
29
|
+
getHRV: ReturnType<typeof makeTool<GetHRVInput>>;
|
|
30
|
+
getSleepSummary: ReturnType<typeof makeTool<GetSleepSummaryInput>>;
|
|
31
|
+
getActivityTotal: ReturnType<typeof makeTool<GetActivityTotalInput>>;
|
|
32
|
+
compareTrend: ReturnType<typeof makeTool<CompareTrendInput>>;
|
|
33
|
+
detectAnomaly: ReturnType<typeof makeTool<DetectAnomalyInput>>;
|
|
34
|
+
getInsights: ReturnType<typeof makeTool<GetInsightsInput>>;
|
|
35
|
+
}
|
|
36
|
+
export interface AnalyticsToolsHandle {
|
|
37
|
+
tools: AnalyticsToolMap;
|
|
38
|
+
setContext: (ctx: ToolContext | null) => void;
|
|
39
|
+
auditWriter: AuditWriter;
|
|
40
|
+
close: () => Promise<void>;
|
|
41
|
+
}
|
|
42
|
+
export declare function createAnalyticsTools(config: AnalyticsToolsConfig): AnalyticsToolsHandle;
|
|
43
|
+
//# sourceMappingURL=factory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../src/tools/factory.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AASH,OAAO,KAAK,EACV,oBAAoB,EACpB,WAAW,EAEX,WAAW,EACZ,MAAM,SAAS,CAAA;AAChB,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAA;AAEjC,OAAO,EAGL,KAAK,qBAAqB,EAC3B,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAGL,KAAK,kBAAkB,EACxB,MAAM,iBAAiB,CAAA;AACxB,OAAO,EAGL,KAAK,iBAAiB,EACvB,MAAM,iBAAiB,CAAA;AACxB,OAAO,EAA6B,KAAK,WAAW,EAAE,MAAM,aAAa,CAAA;AACzE,OAAO,EAGL,KAAK,gBAAgB,EACtB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAGL,KAAK,oBAAoB,EAC1B,MAAM,eAAe,CAAA;AAoBtB,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,UAAU,CAAC,OAAO,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAA;IAChD,eAAe,EAAE,UAAU,CAAC,OAAO,QAAQ,CAAC,oBAAoB,CAAC,CAAC,CAAA;IAClE,gBAAgB,EAAE,UAAU,CAAC,OAAO,QAAQ,CAAC,qBAAqB,CAAC,CAAC,CAAA;IACpE,YAAY,EAAE,UAAU,CAAC,OAAO,QAAQ,CAAC,iBAAiB,CAAC,CAAC,CAAA;IAC5D,aAAa,EAAE,UAAU,CAAC,OAAO,QAAQ,CAAC,kBAAkB,CAAC,CAAC,CAAA;IAC9D,WAAW,EAAE,UAAU,CAAC,OAAO,QAAQ,CAAC,gBAAgB,CAAC,CAAC,CAAA;CAC3D;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,gBAAgB,CAAA;IACvB,UAAU,EAAE,CAAC,GAAG,EAAE,WAAW,GAAG,IAAI,KAAK,IAAI,CAAA;IAC7C,WAAW,EAAE,WAAW,CAAA;IACxB,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAC3B;AAED,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,oBAAoB,GAC3B,oBAAoB,CAiGtB"}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `createAnalyticsTools` — assemble the 6 wrapped AI SDK tools into
|
|
3
|
+
* a single map ready to hand to `streamText({ tools })` / `generateText`.
|
|
4
|
+
*
|
|
5
|
+
* Wires:
|
|
6
|
+
* - rate limiter (or `null` if `rateLimit: false`)
|
|
7
|
+
* - authorize hook (default `familyScopeAuthorize`, optionally with
|
|
8
|
+
* an injected `familyMembersProvider`)
|
|
9
|
+
* - audit writer (batched, optional)
|
|
10
|
+
* - output budget (default 4096 bytes / 100 rows)
|
|
11
|
+
*
|
|
12
|
+
* Per-request context threading: the AI SDK's `execute(input)` has no
|
|
13
|
+
* ctx arg. We hold a mutable `currentCtx` container and expose
|
|
14
|
+
* `setContext(ctx)`. Consumers (zivaone_app) call `setContext` before
|
|
15
|
+
* each LLM turn — matching how `@mongrov/ai`'s `AIProvider` threads
|
|
16
|
+
* per-request scopes. This design keeps rate-limiter buckets + audit
|
|
17
|
+
* writer state shared across requests rather than rebuilding the
|
|
18
|
+
* whole tool map per turn.
|
|
19
|
+
*/
|
|
20
|
+
import { createAuditWriter } from './audit';
|
|
21
|
+
import { familyScopeAuthorize } from './authorize';
|
|
22
|
+
import { createRateLimiter } from './rate-limit';
|
|
23
|
+
import { DEFAULT_OUTPUT_BUDGET, DEFAULT_RATE_LIMIT, } from './types';
|
|
24
|
+
import { makeTool } from './wrap';
|
|
25
|
+
import { getActivityTotal, getActivityTotalInputSchema, } from './impls/activity';
|
|
26
|
+
import { detectAnomaly, detectAnomalyInputSchema, } from './impls/anomaly';
|
|
27
|
+
import { compareTrend, compareTrendInputSchema, } from './impls/compare';
|
|
28
|
+
import { getHRV, getHRVInputSchema } from './impls/hrv';
|
|
29
|
+
import { getInsights, getInsightsInputSchema, } from './impls/insights';
|
|
30
|
+
import { getSleepSummary, getSleepSummaryInputSchema, } from './impls/sleep';
|
|
31
|
+
const DESCRIPTIONS = {
|
|
32
|
+
getHRV: 'Return the requester\'s or a family member\'s average HRV per day '
|
|
33
|
+
+ 'over the last N days (1..90). Args: userId, days.',
|
|
34
|
+
getSleepSummary: 'Return nightly sleep duration and efficiency for a user '
|
|
35
|
+
+ 'over the last N days (1..30). Args: userId, days.',
|
|
36
|
+
getActivityTotal: 'Return total steps and active minutes per day for a user '
|
|
37
|
+
+ 'over the last N days (1..30). Args: userId, days.',
|
|
38
|
+
compareTrend: 'Compare a user\'s metric average across two windows. Args: '
|
|
39
|
+
+ 'userId, metric (hrv_ms|sleep_total_minutes|activity_steps), '
|
|
40
|
+
+ 'currentWindowDays, priorWindowDays (each 1..30).',
|
|
41
|
+
detectAnomaly: 'Flag statistical outliers (>= stddevThreshold from baseline) '
|
|
42
|
+
+ 'in a user\'s metric over the last lookbackDays. Args: userId, metric '
|
|
43
|
+
+ '(hrv_ms|sleep_total_minutes|activity_steps), lookbackDays (7..90), '
|
|
44
|
+
+ 'stddevThreshold (1..4, default 2).',
|
|
45
|
+
getInsights: 'Return recent AI-generated insights for a user. Args: userId, '
|
|
46
|
+
+ 'days (1..30, default 7), optional severity (info|warn|critical).',
|
|
47
|
+
};
|
|
48
|
+
export function createAnalyticsTools(config) {
|
|
49
|
+
const budget = {
|
|
50
|
+
...DEFAULT_OUTPUT_BUDGET,
|
|
51
|
+
...(config.outputBudget ?? {}),
|
|
52
|
+
};
|
|
53
|
+
const rateLimiter = config.rateLimit === false
|
|
54
|
+
? null
|
|
55
|
+
: createRateLimiter({
|
|
56
|
+
config: config.rateLimit ?? DEFAULT_RATE_LIMIT,
|
|
57
|
+
clock: config.clock,
|
|
58
|
+
});
|
|
59
|
+
const authorize = config.authorize
|
|
60
|
+
?? familyScopeAuthorize(config.analytics, {
|
|
61
|
+
familyMembersProvider: config.familyMembersProvider,
|
|
62
|
+
});
|
|
63
|
+
const audit = createAuditWriter({
|
|
64
|
+
analytics: config.analytics,
|
|
65
|
+
enabled: config.audit?.enabled ?? true,
|
|
66
|
+
batchSize: config.audit?.batchSize,
|
|
67
|
+
flushIntervalMs: config.audit?.flushIntervalMs,
|
|
68
|
+
logger: config.logger,
|
|
69
|
+
});
|
|
70
|
+
let currentCtx = null;
|
|
71
|
+
const ctxProvider = () => currentCtx;
|
|
72
|
+
const shared = {
|
|
73
|
+
analytics: config.analytics,
|
|
74
|
+
authorize,
|
|
75
|
+
rateLimiter,
|
|
76
|
+
audit,
|
|
77
|
+
budget,
|
|
78
|
+
logger: config.logger,
|
|
79
|
+
ctxProvider,
|
|
80
|
+
clock: config.clock,
|
|
81
|
+
};
|
|
82
|
+
const tools = {
|
|
83
|
+
getHRV: makeTool({
|
|
84
|
+
...shared,
|
|
85
|
+
name: 'getHRV',
|
|
86
|
+
description: DESCRIPTIONS.getHRV,
|
|
87
|
+
inputSchema: getHRVInputSchema,
|
|
88
|
+
impl: getHRV,
|
|
89
|
+
}),
|
|
90
|
+
getSleepSummary: makeTool({
|
|
91
|
+
...shared,
|
|
92
|
+
name: 'getSleepSummary',
|
|
93
|
+
description: DESCRIPTIONS.getSleepSummary,
|
|
94
|
+
inputSchema: getSleepSummaryInputSchema,
|
|
95
|
+
impl: getSleepSummary,
|
|
96
|
+
}),
|
|
97
|
+
getActivityTotal: makeTool({
|
|
98
|
+
...shared,
|
|
99
|
+
name: 'getActivityTotal',
|
|
100
|
+
description: DESCRIPTIONS.getActivityTotal,
|
|
101
|
+
inputSchema: getActivityTotalInputSchema,
|
|
102
|
+
impl: getActivityTotal,
|
|
103
|
+
}),
|
|
104
|
+
compareTrend: makeTool({
|
|
105
|
+
...shared,
|
|
106
|
+
name: 'compareTrend',
|
|
107
|
+
description: DESCRIPTIONS.compareTrend,
|
|
108
|
+
inputSchema: compareTrendInputSchema,
|
|
109
|
+
impl: compareTrend,
|
|
110
|
+
}),
|
|
111
|
+
detectAnomaly: makeTool({
|
|
112
|
+
...shared,
|
|
113
|
+
name: 'detectAnomaly',
|
|
114
|
+
description: DESCRIPTIONS.detectAnomaly,
|
|
115
|
+
inputSchema: detectAnomalyInputSchema,
|
|
116
|
+
impl: detectAnomaly,
|
|
117
|
+
}),
|
|
118
|
+
getInsights: makeTool({
|
|
119
|
+
...shared,
|
|
120
|
+
name: 'getInsights',
|
|
121
|
+
description: DESCRIPTIONS.getInsights,
|
|
122
|
+
inputSchema: getInsightsInputSchema,
|
|
123
|
+
impl: getInsights,
|
|
124
|
+
}),
|
|
125
|
+
};
|
|
126
|
+
return {
|
|
127
|
+
tools,
|
|
128
|
+
setContext(ctx) {
|
|
129
|
+
currentCtx = ctx;
|
|
130
|
+
},
|
|
131
|
+
auditWriter: audit,
|
|
132
|
+
async close() {
|
|
133
|
+
await audit.close();
|
|
134
|
+
},
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
//# sourceMappingURL=factory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"factory.js","sourceRoot":"","sources":["../../src/tools/factory.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared string helpers for tool output formatting.
|
|
3
|
+
*
|
|
4
|
+
* No I/O; safe to import from any impl. Used to compute the `bytes`
|
|
5
|
+
* field of `ToolResult` and to render deltas consistently across
|
|
6
|
+
* tools.
|
|
7
|
+
*/
|
|
8
|
+
export declare function formatBytes(s: string): number;
|
|
9
|
+
/**
|
|
10
|
+
* Format a signed percentage delta between two numbers. Returns `n/a`
|
|
11
|
+
* when the prior baseline is zero (avoids Infinity / NaN in tool
|
|
12
|
+
* output).
|
|
13
|
+
*/
|
|
14
|
+
export declare function deltaPct(current: number, prior: number): string;
|
|
15
|
+
/** Population stddev; returns 0 when the sample is empty. */
|
|
16
|
+
export declare function popStddev(values: number[]): number;
|
|
17
|
+
//# sourceMappingURL=formatters.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"formatters.d.ts","sourceRoot":"","sources":["../../src/tools/formatters.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAE7C;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAI/D;AAED,6DAA6D;AAC7D,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,CAMlD"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared string helpers for tool output formatting.
|
|
3
|
+
*
|
|
4
|
+
* No I/O; safe to import from any impl. Used to compute the `bytes`
|
|
5
|
+
* field of `ToolResult` and to render deltas consistently across
|
|
6
|
+
* tools.
|
|
7
|
+
*/
|
|
8
|
+
export function formatBytes(s) {
|
|
9
|
+
return new TextEncoder().encode(s).byteLength;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Format a signed percentage delta between two numbers. Returns `n/a`
|
|
13
|
+
* when the prior baseline is zero (avoids Infinity / NaN in tool
|
|
14
|
+
* output).
|
|
15
|
+
*/
|
|
16
|
+
export function deltaPct(current, prior) {
|
|
17
|
+
if (prior === 0)
|
|
18
|
+
return 'n/a';
|
|
19
|
+
const pct = ((current - prior) / prior) * 100;
|
|
20
|
+
return `${pct >= 0 ? '+' : ''}${pct.toFixed(1)}%`;
|
|
21
|
+
}
|
|
22
|
+
/** Population stddev; returns 0 when the sample is empty. */
|
|
23
|
+
export function popStddev(values) {
|
|
24
|
+
if (values.length === 0)
|
|
25
|
+
return 0;
|
|
26
|
+
const mean = values.reduce((a, v) => a + v, 0) / values.length;
|
|
27
|
+
const variance = values.reduce((a, v) => a + (v - mean) ** 2, 0) / values.length;
|
|
28
|
+
return Math.sqrt(variance);
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=formatters.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"formatters.js","sourceRoot":"","sources":["../../src/tools/formatters.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,UAAU,WAAW,CAAC,CAAS;IACnC,OAAO,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,UAAU,CAAA;AAC/C,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,QAAQ,CAAC,OAAe,EAAE,KAAa;IACrD,IAAI,KAAK,KAAK,CAAC;QAAE,OAAO,KAAK,CAAA;IAC7B,MAAM,GAAG,GAAG,CAAC,CAAC,OAAO,GAAG,KAAK,CAAC,GAAG,KAAK,CAAC,GAAG,GAAG,CAAA;IAC7C,OAAO,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAA;AACnD,CAAC;AAED,6DAA6D;AAC7D,MAAM,UAAU,SAAS,CAAC,MAAgB;IACxC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,CAAC,CAAA;IACjC,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,CAAA;IAC9D,MAAM,QAAQ,GACV,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,CAAA;IACnE,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;AAC5B,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { ToolImpl } from '../types';
|
|
3
|
+
export declare const getActivityTotalInputSchema: 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 GetActivityTotalInput = z.infer<typeof getActivityTotalInputSchema>;
|
|
14
|
+
export declare const getActivityTotal: ToolImpl<GetActivityTotalInput>;
|
|
15
|
+
//# sourceMappingURL=activity.d.ts.map
|
|
@@ -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
|