catence 0.2.0-beta.23 → 0.2.0-beta.24
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/contracts/release.d.ts +1 -1
- package/dist/contracts/release.js +1 -1
- package/dist/core/query/activity-discovery.d.ts +2 -0
- package/dist/core/query/activity-discovery.js +1 -1
- package/dist/core/query/catalog.js +1 -1
- package/dist/core/query/swimming.js +2 -2
- package/dist/interfaces/mcp/server.js +2 -0
- package/package.json +1 -1
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Keep these values synchronized with release/manifest.json; release checks
|
|
4
4
|
* deliberately reject drift before a package can be published.
|
|
5
5
|
*/
|
|
6
|
-
export declare const CATENCE_RUNTIME_VERSION = "0.2.0-beta.
|
|
6
|
+
export declare const CATENCE_RUNTIME_VERSION = "0.2.0-beta.24";
|
|
7
7
|
export declare const CATENCE_PROTOCOL_VERSION = 1;
|
|
8
8
|
export type CatenceRuntimeCapabilities = {
|
|
9
9
|
mcp: true;
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Keep these values synchronized with release/manifest.json; release checks
|
|
4
4
|
* deliberately reject drift before a package can be published.
|
|
5
5
|
*/
|
|
6
|
-
export const CATENCE_RUNTIME_VERSION = '0.2.0-beta.
|
|
6
|
+
export const CATENCE_RUNTIME_VERSION = '0.2.0-beta.24';
|
|
7
7
|
export const CATENCE_PROTOCOL_VERSION = 1;
|
|
8
8
|
export function catenceRuntimeHealth() {
|
|
9
9
|
return {
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { ReadOnlyRepository } from './repository.js';
|
|
2
2
|
export type FindActivitiesRequest = {
|
|
3
3
|
sports?: string[];
|
|
4
|
+
/** Accepted as a convenience alias for `sports: [sport]`. */
|
|
5
|
+
sport?: string;
|
|
4
6
|
distanceKm?: [number, number];
|
|
5
7
|
nameContains?: string[];
|
|
6
8
|
startDate?: string;
|
|
@@ -49,7 +49,7 @@ export class ActivityDiscoveryService {
|
|
|
49
49
|
this.repository = repository;
|
|
50
50
|
}
|
|
51
51
|
async findActivities(request = {}) {
|
|
52
|
-
const sports = [...new Set((request.sports ?? []).map((sport) => sport.trim()).filter(Boolean))];
|
|
52
|
+
const sports = [...new Set((request.sports ?? (request.sport ? [request.sport] : [])).map((sport) => sport.trim()).filter(Boolean))];
|
|
53
53
|
const names = [...new Set((request.nameContains ?? []).map((name) => name.trim()).filter(Boolean))];
|
|
54
54
|
if (sports.length > 12 || names.length > 12)
|
|
55
55
|
throw new QueryValidationError('sports and nameContains each permit at most 12 values.');
|
|
@@ -25,7 +25,7 @@ export const DATASET_CATALOG = {
|
|
|
25
25
|
columns: [column('activity_source_id', 'VARCHAR', { filterable: true, groupable: true }), column('activity_id', 'VARCHAR', { filterable: true, groupable: true }), column('provider', 'VARCHAR', { filterable: true, groupable: true }), column('started_at_utc', 'TIMESTAMPTZ', { filterable: true }), column('sport', 'VARCHAR', { filterable: true, groupable: true }), column('interval_key', 'VARCHAR', { filterable: true }), column('label', 'VARCHAR', { filterable: true, groupable: true }), column('source_type', 'VARCHAR', { filterable: true, groupable: true }), column('start_s', 'DOUBLE', { unit: 's', metric: true }), column('end_s', 'DOUBLE', { unit: 's', metric: true }), column('duration_s', 'DOUBLE', { unit: 's', metric: true }), column('moving_s', 'DOUBLE', { unit: 's', metric: true }), column('distance_m', 'DOUBLE', { unit: 'm', metric: true }), column('avg_power', 'DOUBLE', { unit: 'W', metric: true }), column('avg_hr', 'DOUBLE', { unit: 'bpm', metric: true }), column('avg_pace', 'DOUBLE', { metric: true }), column('intensity', 'DOUBLE', { metric: true })],
|
|
26
26
|
},
|
|
27
27
|
swim_lengths: {
|
|
28
|
-
name: 'swim_lengths', relation: 'swim_length_facts', description: 'Explicit provider-supplied swim-length facts. An empty result means lengths were not supplied; Catence does not infer them from activity samples.', dateColumn: 'started_at_utc', provenanceColumns: ['activity_source_id', 'source', 'confidence', 'raw_object_hash'], permittedGroupings: ['provider', 'sport', 'source', 'confidence', 'is_rest'],
|
|
28
|
+
name: 'swim_lengths', relation: 'swim_length_facts', description: 'Explicit provider-supplied swim-length facts. An empty result means lengths were not supplied; Catence does not infer them from activity samples. Per-length swolf may be null (not supplied by all providers); derive as duration_s + stroke_count. Per-length distance is typically null in Garmin FIT; use pool_length_m to compute pace (duration_s / pool_length_m * 100 for s/100m).', dateColumn: 'started_at_utc', provenanceColumns: ['activity_source_id', 'source', 'confidence', 'raw_object_hash'], permittedGroupings: ['provider', 'sport', 'source', 'confidence', 'is_rest'],
|
|
29
29
|
columns: [column('activity_source_id', 'VARCHAR', { filterable: true, groupable: true }), column('activity_id', 'VARCHAR', { filterable: true, groupable: true }), column('provider', 'VARCHAR', { filterable: true, groupable: true }), column('started_at_utc', 'TIMESTAMPTZ', { filterable: true }), column('sport', 'VARCHAR', { filterable: true, groupable: true }), column('source', 'VARCHAR', { filterable: true, groupable: true }), column('confidence', 'VARCHAR', { filterable: true, groupable: true }), column('length_index', 'INTEGER', { filterable: true, groupable: true }), column('lap_index', 'INTEGER', { filterable: true, groupable: true }), column('pool_length_m', 'DOUBLE', { unit: 'm', metric: true }), column('start_time', 'TIMESTAMPTZ', { filterable: true }), column('duration_s', 'DOUBLE', { unit: 's', metric: true }), column('active_duration_s', 'DOUBLE', { unit: 's', metric: true }), column('distance_m', 'DOUBLE', { unit: 'm', metric: true }), column('stroke_count', 'DOUBLE', { unit: 'count', metric: true }), column('stroke_rate', 'DOUBLE', { unit: 'strokes/min', metric: true }), column('swolf', 'DOUBLE', { metric: true }), column('avg_hr', 'DOUBLE', { unit: 'bpm', metric: true }), column('max_hr', 'DOUBLE', { unit: 'bpm', metric: true }), column('is_rest', 'BOOLEAN', { filterable: true, groupable: true }), column('raw_object_hash', 'VARCHAR')],
|
|
30
30
|
},
|
|
31
31
|
swim_sets: {
|
|
@@ -86,7 +86,7 @@ export class SwimmingService {
|
|
|
86
86
|
availability: {
|
|
87
87
|
explicitLengthFacts: lengths.length > 0,
|
|
88
88
|
setFacts: sets.length > 0,
|
|
89
|
-
perLengthPaceAvailable: lengths.some((length) => Number(length.duration_s) > 0
|
|
89
|
+
perLengthPaceAvailable: lengths.some((length) => Number(length.duration_s) > 0),
|
|
90
90
|
},
|
|
91
91
|
};
|
|
92
92
|
}));
|
|
@@ -123,7 +123,7 @@ export class SwimmingService {
|
|
|
123
123
|
FROM activity_quality_flags GROUP BY activity_source_id
|
|
124
124
|
), lengths AS (
|
|
125
125
|
SELECT activity_source_id, count(*) AS length_fact_count,
|
|
126
|
-
count(*) FILTER (WHERE duration_s > 0
|
|
126
|
+
count(*) FILTER (WHERE duration_s > 0) AS pace_length_fact_count
|
|
127
127
|
FROM swim_lengths GROUP BY activity_source_id
|
|
128
128
|
), sets AS (
|
|
129
129
|
SELECT activity_source_id, count(*) AS set_fact_count FROM swim_sets GROUP BY activity_source_id
|
|
@@ -32,6 +32,7 @@ const MCP_INSTRUCTIONS = [
|
|
|
32
32
|
'When summary metric is NULL (e.g. avg_power), fall through to FIT-derived/samples layer before declaring absence.',
|
|
33
33
|
'Before reusing any prior course/elevation profile, resolve event\'s courseId and diff it against past activity\'s course; different courseId => different profile.',
|
|
34
34
|
'If a needed tool is not visible in your current tool list (the host may truncate it to a subset), read the catence://tools resource to discover all available tools before concluding a capability is absent.',
|
|
35
|
+
'For swim analysis: use find_activities (sports: ["lap_swimming"] or sport: "lap_swimming") to list pool sessions. Use get_swim_laps for per-length data (pace from duration_s/pool_length_m, SPL from stroke_count, SWOLF from duration_s+stroke_count). Use swim_progress_report for session-level SWOLF, fastest-100, and stroke cadence. Per-length SWOLF is derived when the provider does not supply it. Per-length distance is typically null in Garmin FIT; use pool_length_m for pace calculations.',
|
|
35
36
|
].join(' ');
|
|
36
37
|
function textResult(value) {
|
|
37
38
|
return { content: [{ type: 'text', text: JSON.stringify(jsonSafe(value), null, 2) }] };
|
|
@@ -460,6 +461,7 @@ export function createCatenceMcpServer(paths = resolveCatalogPaths(), dependenci
|
|
|
460
461
|
title: 'Find activities and likely races',
|
|
461
462
|
description: 'Find canonical activities by sport, distance, name, and date. Results are paginated and transparently flag likely-race signals without claiming provider-confirmed race metadata. Read-only.',
|
|
462
463
|
inputSchema: {
|
|
464
|
+
sport: z.string().min(1).optional(),
|
|
463
465
|
sports: z.array(z.string().min(1)).min(1).max(12).optional(),
|
|
464
466
|
distanceKm: z.tuple([z.number().min(0), z.number().min(0)]).optional(),
|
|
465
467
|
nameContains: z.array(z.string().min(1)).min(1).max(12).optional(),
|
package/package.json
CHANGED