forge-openclaw-plugin 0.2.92 → 0.2.94
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/assets/{board-DKxKOwax.js → board-D1HbyD4u.js} +1 -1
- package/dist/assets/{index-BNvUaA6y.js → index-DWZd3qT-.js} +44 -44
- package/dist/assets/index-PA_Ih223.css +1 -0
- package/dist/assets/{motion-CM4AfIqo.js → motion-D2OqILg_.js} +1 -1
- package/dist/assets/{table-BUeQ9wzR.js → table-YWWjPjC_.js} +1 -1
- package/dist/assets/{ui-3Wd4pVaA.js → ui-DikPZj8S.js} +1 -1
- package/dist/assets/vendor-BS9OPVNh.js +2181 -0
- package/dist/companion-iroh/darwin-arm64/forge-companion-iroh +0 -0
- package/dist/companion-iroh/darwin-x64/forge-companion-iroh +0 -0
- package/dist/companion-iroh/linux-x64/forge-companion-iroh +0 -0
- package/dist/index.html +7 -7
- package/dist/openclaw/parity.js +1 -0
- package/dist/openclaw/routes.js +5 -0
- package/dist/openclaw/tools.js +7 -0
- package/dist/server/server/migrations/064_health_workout_time_series_identity.sql +161 -0
- package/dist/server/server/src/app.js +72 -2
- package/dist/server/server/src/health-workout-analytics.js +8 -2
- package/dist/server/server/src/health.js +337 -2
- package/dist/server/server/src/openapi.js +59 -0
- package/dist/server/src/lib/api.js +6 -0
- package/openclaw.plugin.json +2 -1
- package/package.json +1 -1
- package/server/migrations/064_health_workout_time_series_identity.sql +161 -0
- package/skills/forge-openclaw/SKILL.md +25 -7
- package/skills/forge-openclaw/entity_conversation_playbooks.md +88 -12
- package/skills/forge-openclaw/psyche_entity_playbooks.md +35 -0
- package/dist/assets/index-NqIbz_lv.css +0 -1
- package/dist/assets/vendor-BVU0cZC9.js +0 -2171
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/dist/index.html
CHANGED
|
@@ -13,14 +13,14 @@
|
|
|
13
13
|
/>
|
|
14
14
|
<link rel="icon" type="image/png" href="/forge/assets/favicon-BCHm9dUV.ico" />
|
|
15
15
|
<link rel="alternate icon" href="/forge/assets/favicon-BCHm9dUV.ico" />
|
|
16
|
-
<script type="module" crossorigin src="/forge/assets/index-
|
|
17
|
-
<link rel="modulepreload" crossorigin href="/forge/assets/vendor-
|
|
18
|
-
<link rel="modulepreload" crossorigin href="/forge/assets/board-
|
|
19
|
-
<link rel="modulepreload" crossorigin href="/forge/assets/ui-
|
|
20
|
-
<link rel="modulepreload" crossorigin href="/forge/assets/motion-
|
|
21
|
-
<link rel="modulepreload" crossorigin href="/forge/assets/table-
|
|
16
|
+
<script type="module" crossorigin src="/forge/assets/index-DWZd3qT-.js"></script>
|
|
17
|
+
<link rel="modulepreload" crossorigin href="/forge/assets/vendor-BS9OPVNh.js">
|
|
18
|
+
<link rel="modulepreload" crossorigin href="/forge/assets/board-D1HbyD4u.js">
|
|
19
|
+
<link rel="modulepreload" crossorigin href="/forge/assets/ui-DikPZj8S.js">
|
|
20
|
+
<link rel="modulepreload" crossorigin href="/forge/assets/motion-D2OqILg_.js">
|
|
21
|
+
<link rel="modulepreload" crossorigin href="/forge/assets/table-YWWjPjC_.js">
|
|
22
22
|
<link rel="stylesheet" crossorigin href="/forge/assets/vendor-B-Lq_OG3.css">
|
|
23
|
-
<link rel="stylesheet" crossorigin href="/forge/assets/index-
|
|
23
|
+
<link rel="stylesheet" crossorigin href="/forge/assets/index-PA_Ih223.css">
|
|
24
24
|
</head>
|
|
25
25
|
<body class="bg-canvas text-ink antialiased">
|
|
26
26
|
<div id="root"></div>
|
package/dist/openclaw/parity.js
CHANGED
|
@@ -30,6 +30,7 @@ export const FORGE_SUPPORTED_PLUGIN_API_ROUTES = [
|
|
|
30
30
|
{ method: "GET", path: "/api/v1/health/sleep", purpose: "health" },
|
|
31
31
|
{ method: "PATCH", path: "/api/v1/health/sleep/:id", purpose: "health" },
|
|
32
32
|
{ method: "GET", path: "/api/v1/health/fitness", purpose: "health" },
|
|
33
|
+
{ method: "GET", path: "/api/v1/health/training-load", purpose: "health" },
|
|
33
34
|
{ method: "PATCH", path: "/api/v1/health/workouts/:id", purpose: "health" },
|
|
34
35
|
{ method: "GET", path: "/api/v1/movement/day", purpose: "movement" },
|
|
35
36
|
{ method: "GET", path: "/api/v1/movement/month", purpose: "movement" },
|
package/dist/openclaw/routes.js
CHANGED
|
@@ -176,6 +176,11 @@ export const FORGE_PLUGIN_ROUTE_GROUPS = [
|
|
|
176
176
|
upstreamPath: "/api/v1/health/fitness",
|
|
177
177
|
target: (_match, url) => passthroughSearch("/api/v1/health/fitness", url)
|
|
178
178
|
}),
|
|
179
|
+
exact("/forge/v1/health/training-load", {
|
|
180
|
+
method: "GET",
|
|
181
|
+
upstreamPath: "/api/v1/health/training-load",
|
|
182
|
+
target: (_match, url) => passthroughSearch("/api/v1/health/training-load", url)
|
|
183
|
+
}),
|
|
179
184
|
{
|
|
180
185
|
path: "/forge/v1/movement",
|
|
181
186
|
match: "prefix",
|
package/dist/openclaw/tools.js
CHANGED
|
@@ -752,6 +752,13 @@ export function registerForgePluginTools(api, config) {
|
|
|
752
752
|
parameters: scopedReadSchema,
|
|
753
753
|
path: (params) => withUserIds("/api/v1/health/fitness", params.userIds)
|
|
754
754
|
});
|
|
755
|
+
registerReadTool(api, config, {
|
|
756
|
+
name: "forge_get_training_load_overview",
|
|
757
|
+
label: "Forge Training Load Overview",
|
|
758
|
+
description: "Read the cardiovascular training-load surface with acute/chronic load, HR zone distribution, weekly intensity targets, and data-quality flags.",
|
|
759
|
+
parameters: scopedReadSchema,
|
|
760
|
+
path: (params) => withUserIds("/api/v1/health/training-load", params.userIds)
|
|
761
|
+
});
|
|
755
762
|
api.registerTool({
|
|
756
763
|
name: "forge_update_sleep_session",
|
|
757
764
|
label: "Forge Update Sleep Session",
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
CREATE TEMP TABLE health_workout_time_series_dedupe AS
|
|
2
|
+
SELECT
|
|
3
|
+
workout_id,
|
|
4
|
+
metric_key,
|
|
5
|
+
source_sample_uid,
|
|
6
|
+
MIN(rowid) AS survivor_rowid,
|
|
7
|
+
(
|
|
8
|
+
SELECT latest.rowid
|
|
9
|
+
FROM health_workout_time_series AS latest
|
|
10
|
+
WHERE latest.workout_id = grouped.workout_id
|
|
11
|
+
AND latest.metric_key = grouped.metric_key
|
|
12
|
+
AND latest.source_sample_uid = grouped.source_sample_uid
|
|
13
|
+
ORDER BY latest.updated_at DESC, latest.created_at DESC, latest.rowid DESC
|
|
14
|
+
LIMIT 1
|
|
15
|
+
) AS latest_rowid,
|
|
16
|
+
(
|
|
17
|
+
SELECT latest.series_index
|
|
18
|
+
FROM health_workout_time_series AS latest
|
|
19
|
+
WHERE latest.workout_id = grouped.workout_id
|
|
20
|
+
AND latest.metric_key = grouped.metric_key
|
|
21
|
+
AND latest.source_sample_uid = grouped.source_sample_uid
|
|
22
|
+
ORDER BY latest.updated_at DESC, latest.created_at DESC, latest.rowid DESC
|
|
23
|
+
LIMIT 1
|
|
24
|
+
) AS latest_series_index
|
|
25
|
+
FROM health_workout_time_series AS grouped
|
|
26
|
+
WHERE source_sample_uid IS NOT NULL
|
|
27
|
+
AND source_sample_uid != ''
|
|
28
|
+
GROUP BY workout_id, metric_key, source_sample_uid
|
|
29
|
+
HAVING COUNT(*) > 1;
|
|
30
|
+
|
|
31
|
+
UPDATE health_workout_time_series
|
|
32
|
+
SET
|
|
33
|
+
label = (
|
|
34
|
+
SELECT latest.label
|
|
35
|
+
FROM health_workout_time_series AS latest
|
|
36
|
+
JOIN health_workout_time_series_dedupe AS dedupe
|
|
37
|
+
ON dedupe.latest_rowid = latest.rowid
|
|
38
|
+
WHERE dedupe.survivor_rowid = health_workout_time_series.rowid
|
|
39
|
+
),
|
|
40
|
+
category = (
|
|
41
|
+
SELECT latest.category
|
|
42
|
+
FROM health_workout_time_series AS latest
|
|
43
|
+
JOIN health_workout_time_series_dedupe AS dedupe
|
|
44
|
+
ON dedupe.latest_rowid = latest.rowid
|
|
45
|
+
WHERE dedupe.survivor_rowid = health_workout_time_series.rowid
|
|
46
|
+
),
|
|
47
|
+
unit = (
|
|
48
|
+
SELECT latest.unit
|
|
49
|
+
FROM health_workout_time_series AS latest
|
|
50
|
+
JOIN health_workout_time_series_dedupe AS dedupe
|
|
51
|
+
ON dedupe.latest_rowid = latest.rowid
|
|
52
|
+
WHERE dedupe.survivor_rowid = health_workout_time_series.rowid
|
|
53
|
+
),
|
|
54
|
+
value = (
|
|
55
|
+
SELECT latest.value
|
|
56
|
+
FROM health_workout_time_series AS latest
|
|
57
|
+
JOIN health_workout_time_series_dedupe AS dedupe
|
|
58
|
+
ON dedupe.latest_rowid = latest.rowid
|
|
59
|
+
WHERE dedupe.survivor_rowid = health_workout_time_series.rowid
|
|
60
|
+
),
|
|
61
|
+
started_at = (
|
|
62
|
+
SELECT latest.started_at
|
|
63
|
+
FROM health_workout_time_series AS latest
|
|
64
|
+
JOIN health_workout_time_series_dedupe AS dedupe
|
|
65
|
+
ON dedupe.latest_rowid = latest.rowid
|
|
66
|
+
WHERE dedupe.survivor_rowid = health_workout_time_series.rowid
|
|
67
|
+
),
|
|
68
|
+
ended_at = (
|
|
69
|
+
SELECT latest.ended_at
|
|
70
|
+
FROM health_workout_time_series AS latest
|
|
71
|
+
JOIN health_workout_time_series_dedupe AS dedupe
|
|
72
|
+
ON dedupe.latest_rowid = latest.rowid
|
|
73
|
+
WHERE dedupe.survivor_rowid = health_workout_time_series.rowid
|
|
74
|
+
),
|
|
75
|
+
source_device = (
|
|
76
|
+
SELECT latest.source_device
|
|
77
|
+
FROM health_workout_time_series AS latest
|
|
78
|
+
JOIN health_workout_time_series_dedupe AS dedupe
|
|
79
|
+
ON dedupe.latest_rowid = latest.rowid
|
|
80
|
+
WHERE dedupe.survivor_rowid = health_workout_time_series.rowid
|
|
81
|
+
),
|
|
82
|
+
source_bundle_identifier = (
|
|
83
|
+
SELECT latest.source_bundle_identifier
|
|
84
|
+
FROM health_workout_time_series AS latest
|
|
85
|
+
JOIN health_workout_time_series_dedupe AS dedupe
|
|
86
|
+
ON dedupe.latest_rowid = latest.rowid
|
|
87
|
+
WHERE dedupe.survivor_rowid = health_workout_time_series.rowid
|
|
88
|
+
),
|
|
89
|
+
source_product_type = (
|
|
90
|
+
SELECT latest.source_product_type
|
|
91
|
+
FROM health_workout_time_series AS latest
|
|
92
|
+
JOIN health_workout_time_series_dedupe AS dedupe
|
|
93
|
+
ON dedupe.latest_rowid = latest.rowid
|
|
94
|
+
WHERE dedupe.survivor_rowid = health_workout_time_series.rowid
|
|
95
|
+
),
|
|
96
|
+
capture_method = (
|
|
97
|
+
SELECT latest.capture_method
|
|
98
|
+
FROM health_workout_time_series AS latest
|
|
99
|
+
JOIN health_workout_time_series_dedupe AS dedupe
|
|
100
|
+
ON dedupe.latest_rowid = latest.rowid
|
|
101
|
+
WHERE dedupe.survivor_rowid = health_workout_time_series.rowid
|
|
102
|
+
),
|
|
103
|
+
quality_flags_json = (
|
|
104
|
+
SELECT latest.quality_flags_json
|
|
105
|
+
FROM health_workout_time_series AS latest
|
|
106
|
+
JOIN health_workout_time_series_dedupe AS dedupe
|
|
107
|
+
ON dedupe.latest_rowid = latest.rowid
|
|
108
|
+
WHERE dedupe.survivor_rowid = health_workout_time_series.rowid
|
|
109
|
+
),
|
|
110
|
+
metadata_json = (
|
|
111
|
+
SELECT latest.metadata_json
|
|
112
|
+
FROM health_workout_time_series AS latest
|
|
113
|
+
JOIN health_workout_time_series_dedupe AS dedupe
|
|
114
|
+
ON dedupe.latest_rowid = latest.rowid
|
|
115
|
+
WHERE dedupe.survivor_rowid = health_workout_time_series.rowid
|
|
116
|
+
),
|
|
117
|
+
provenance_json = (
|
|
118
|
+
SELECT latest.provenance_json
|
|
119
|
+
FROM health_workout_time_series AS latest
|
|
120
|
+
JOIN health_workout_time_series_dedupe AS dedupe
|
|
121
|
+
ON dedupe.latest_rowid = latest.rowid
|
|
122
|
+
WHERE dedupe.survivor_rowid = health_workout_time_series.rowid
|
|
123
|
+
),
|
|
124
|
+
updated_at = (
|
|
125
|
+
SELECT latest.updated_at
|
|
126
|
+
FROM health_workout_time_series AS latest
|
|
127
|
+
JOIN health_workout_time_series_dedupe AS dedupe
|
|
128
|
+
ON dedupe.latest_rowid = latest.rowid
|
|
129
|
+
WHERE dedupe.survivor_rowid = health_workout_time_series.rowid
|
|
130
|
+
)
|
|
131
|
+
WHERE rowid IN (
|
|
132
|
+
SELECT survivor_rowid
|
|
133
|
+
FROM health_workout_time_series_dedupe
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
DELETE FROM health_workout_time_series
|
|
137
|
+
WHERE rowid IN (
|
|
138
|
+
SELECT duplicate.rowid
|
|
139
|
+
FROM health_workout_time_series AS duplicate
|
|
140
|
+
JOIN health_workout_time_series_dedupe AS dedupe
|
|
141
|
+
ON dedupe.workout_id = duplicate.workout_id
|
|
142
|
+
AND dedupe.metric_key = duplicate.metric_key
|
|
143
|
+
AND dedupe.source_sample_uid = duplicate.source_sample_uid
|
|
144
|
+
WHERE duplicate.rowid != dedupe.survivor_rowid
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
UPDATE health_workout_time_series
|
|
148
|
+
SET series_index = (
|
|
149
|
+
SELECT dedupe.latest_series_index
|
|
150
|
+
FROM health_workout_time_series_dedupe AS dedupe
|
|
151
|
+
WHERE dedupe.survivor_rowid = health_workout_time_series.rowid
|
|
152
|
+
)
|
|
153
|
+
WHERE rowid IN (
|
|
154
|
+
SELECT survivor_rowid
|
|
155
|
+
FROM health_workout_time_series_dedupe
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
DROP TABLE health_workout_time_series_dedupe;
|
|
159
|
+
|
|
160
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_health_workout_time_series_sample_identity
|
|
161
|
+
ON health_workout_time_series(workout_id, metric_key, source_sample_uid);
|
|
@@ -66,7 +66,7 @@ import { registerWebRoutes } from "./web.js";
|
|
|
66
66
|
import { createManagerRuntime } from "./managers/runtime.js";
|
|
67
67
|
import { isManagerError } from "./managers/type-guards.js";
|
|
68
68
|
import { buildCompanionPairingTransport, getCompanionIrohStatus, stopCompanionIroh } from "./services/companion-iroh.js";
|
|
69
|
-
import { createCompanionPairingSession, createCompanionPairingSessionSchema, createSleepSession, createSleepSessionSchema, createWorkoutSession, createWorkoutSessionSchema, deleteSleepSession, deleteWorkoutSession, getCompanionPairingSessionById, getCompanionOverview, getFitnessViewData, getSleepSessionById, getSleepSessionDetailById, getSleepTimelineOverlaysForRange, getSleepViewData, getVitalsViewData, getHealthZoneProfileForUser, getMobileHealthSyncSessionStatus, getWorkoutSessionById, getWorkoutSessionDetailById, heartbeatCompanionPairing, heartbeatCompanionPairingSchema, healthZoneProfilePatchSchema, abortMobileHealthSyncSession, completeMobileHealthSyncSession, ingestMobileHealthSync, ingestMobileHealthSyncChunk, mobileHealthSyncChunkSchema, mobileHealthSyncSessionCompleteSchema, mobileHealthSyncSessionStartSchema, mobileHealthSyncSchema, patchHealthZoneProfileForUser, patchCompanionPairingSourceState, patchCompanionPairingSourceStateSchema, companionSourceKeySchema, requireValidPairing, startMobileHealthSyncSession, revokeAllCompanionPairingSessions, revokeAllCompanionPairingSessionsSchema, revokeCompanionPairingSession, updateMobileCompanionSourceState, updateMobileCompanionSourceStateSchema, verifyCompanionPairing, verifyCompanionPairingSchema, updateSleepMetadata, updateSleepMetadataSchema, updateWorkoutMetadata, updateWorkoutMetadataSchema } from "./health.js";
|
|
69
|
+
import { createCompanionPairingSession, createCompanionPairingSessionSchema, createSleepSession, createSleepSessionSchema, createWorkoutSession, createWorkoutSessionSchema, deleteSleepSession, deleteWorkoutSession, getCompanionPairingSessionById, getCompanionOverview, getFitnessViewData, getSleepSessionById, getSleepSessionDetailById, getSleepTimelineOverlaysForRange, getSleepViewData, getTrainingLoadViewData, getVitalsViewData, getHealthZoneProfileForUser, getMobileHealthSyncSessionStatus, getWorkoutSessionById, getWorkoutSessionDetailById, heartbeatCompanionPairing, heartbeatCompanionPairingSchema, healthZoneProfilePatchSchema, abortMobileHealthSyncSession, completeMobileHealthSyncSession, ingestMobileHealthSync, ingestMobileHealthSyncChunk, mobileHealthSyncChunkSchema, mobileHealthSyncSessionCompleteSchema, mobileHealthSyncSessionStartSchema, mobileHealthSyncSchema, patchHealthZoneProfileForUser, patchCompanionPairingSourceState, patchCompanionPairingSourceStateSchema, companionSourceKeySchema, requireValidPairing, startMobileHealthSyncSession, revokeAllCompanionPairingSessions, revokeAllCompanionPairingSessionsSchema, revokeCompanionPairingSession, updateMobileCompanionSourceState, updateMobileCompanionSourceStateSchema, verifyCompanionPairing, verifyCompanionPairingSchema, updateSleepMetadata, updateSleepMetadataSchema, updateWorkoutMetadata, updateWorkoutMetadataSchema } from "./health.js";
|
|
70
70
|
import { analyzeMovementUserBoxPreflight, createMovementUserBox, createMovementPlace, deleteMovementUserBox, getMovementAllTimeSummary, getMovementBoxDetail, getMovementDayDetail, getMovementMobileBootstrap, getMovementTimeline, getMovementSelectionAggregate, getMovementSettings, getMovementTripDetail, getMovementMonthSummary, invalidateAutomaticMovementBox, listMovementPlaces, movementAutomaticBoxInvalidateSchema, movementMobileBootstrapSchema, movementMobilePlaceMutationSchema, movementMobileStayPatchSchema, movementMobileUserBoxCreateSchema, movementMobileUserBoxPreflightSchema, movementMobileUserBoxPatchSchema, movementMobileAutomaticBoxInvalidateSchema, movementMobileTimelineSchema, movementPlaceMutationSchema, movementPlacePatchSchema, movementSelectionAggregateSchema, movementStayPatchSchema, movementTripPatchSchema, movementUserBoxCreateSchema, movementUserBoxPreflightSchema, movementUserBoxPatchSchema, movementSettingsPatchSchema, movementTimelineQuerySchema, movementTripPointPatchSchema, deleteMovementStay, deleteMovementTrip, deleteMovementTripPoint, updateMovementPlace, updateMovementSettings, updateMovementStay, updateMovementTrip, updateMovementUserBox, updateMovementTripPoint, resolveMovementTimelineSegmentForBox } from "./movement.js";
|
|
71
71
|
import { getScreenTimeAllTimeSummary, getScreenTimeDayDetail, getScreenTimeMonthSummary, getScreenTimeSettings, screenTimeSettingsPatchSchema, updateScreenTimeSettings } from "./screen-time.js";
|
|
72
72
|
import { assertWatchReady, buildWatchBootstrap, ingestWatchCaptureBatch, mobileWatchBootstrapSchema, mobileWatchCaptureBatchSchema, mobileWatchHabitCheckInSchema } from "./watch-mobile.js";
|
|
@@ -2246,6 +2246,8 @@ function buildPreferredMutationPath(entityType) {
|
|
|
2246
2246
|
return "Read-only surface. Use batch CRUD for sleep_session records or the review enrichment route for reflective notes.";
|
|
2247
2247
|
case "sports_overview":
|
|
2248
2248
|
return "Read-only surface. Use batch CRUD for workout_session records or the review enrichment route for reflective notes.";
|
|
2249
|
+
case "training_load":
|
|
2250
|
+
return "Read-only surface. Use it for cardiovascular load, HR zones, acute/chronic stress, VO2max context, and target analysis; use batch CRUD for underlying workout_session records.";
|
|
2249
2251
|
default:
|
|
2250
2252
|
return "Read-only surface.";
|
|
2251
2253
|
}
|
|
@@ -2317,6 +2319,8 @@ function buildPreferredReadPath(entityType) {
|
|
|
2317
2319
|
return "/api/v1/health/sleep";
|
|
2318
2320
|
case "sports_overview":
|
|
2319
2321
|
return "/api/v1/health/fitness";
|
|
2322
|
+
case "training_load":
|
|
2323
|
+
return "/api/v1/health/training-load";
|
|
2320
2324
|
default:
|
|
2321
2325
|
return null;
|
|
2322
2326
|
}
|
|
@@ -3037,6 +3041,19 @@ const AGENT_ONBOARDING_ENTITY_CATALOG = [
|
|
|
3037
3041
|
"Read this surface before suggesting workout reflections or recovery follow-up."
|
|
3038
3042
|
],
|
|
3039
3043
|
fieldGuide: []
|
|
3044
|
+
}),
|
|
3045
|
+
enrichOnboardingEntityGuide({
|
|
3046
|
+
entityType: "training_load",
|
|
3047
|
+
purpose: "The read-model cardiovascular training-load workspace for acute/chronic load, HR zone distribution, intensity targets, and VO2max context.",
|
|
3048
|
+
minimumCreateFields: [],
|
|
3049
|
+
relationshipRules: [
|
|
3050
|
+
"Use this surface for review and interpretation.",
|
|
3051
|
+
"Create, update, delete, or search the underlying workout_session records through batch CRUD by default."
|
|
3052
|
+
],
|
|
3053
|
+
searchHints: [
|
|
3054
|
+
"Read this surface before advising on high-intensity balance, recovery load, or cardiovascular training targets."
|
|
3055
|
+
],
|
|
3056
|
+
fieldGuide: []
|
|
3040
3057
|
})
|
|
3041
3058
|
];
|
|
3042
3059
|
const AGENT_ONBOARDING_CONVERSATION_RULES = [
|
|
@@ -3060,6 +3077,8 @@ const AGENT_ONBOARDING_CONVERSATION_RULES = [
|
|
|
3060
3077
|
"For direct update or review requests, the next question should usually narrow the saved object, timeframe, or route family instead of reopening the whole meaning-making arc.",
|
|
3061
3078
|
"For updates, start with the smallest thing that now feels wrong, newly true, or newly visible rather than restarting the whole story.",
|
|
3062
3079
|
"For review requests, ask what practical question the user wants the read to answer before you ask for more scope.",
|
|
3080
|
+
"Treat userId, owner, and human/bot assignees as accountability and scope, not as opening form fields. Ask whose record or owner scope matters only when it changes visibility, review results, collaboration, automation behavior, or later filtering.",
|
|
3081
|
+
"For read and overview requests, ask for human or bot user scope only when the answer would meaningfully differ across owners; otherwise keep the next question focused on the user's practical question.",
|
|
3063
3082
|
"The opening question should help the user understand what they are actually trying to save, decide, review, or change, not make them perform the schema out loud.",
|
|
3064
3083
|
"If the user already named the exact correction in usable language, confirm only the missing scope, timing, or route-selecting detail that still matters, then act.",
|
|
3065
3084
|
"Keep API and architecture nouns out of user-facing questions unless the user asks about implementation. Do not ask the user about surfaces, route families, CRUD, payloads, mutation paths, or read paths; ask about the human object such as a wiki page, note, trigger report, behavior pattern, belief, mode, movement timeline, energy model, weekday pattern, flow, run, or node result.",
|
|
@@ -3080,7 +3099,7 @@ const AGENT_ONBOARDING_CONVERSATION_RULES = [
|
|
|
3080
3099
|
"Once the route family is clear, say it plainly enough that another agent could follow the same path without guessing.",
|
|
3081
3100
|
"For Movement specifically, treat missing-data corrections as user-defined overlay boxes unless the user is editing an already-recorded stay or trip. When the user already gave a clear instruction like 'that missing block was home', act after only the last ambiguity is resolved.",
|
|
3082
3101
|
"For action workflows such as task_run, work_adjustment, questionnaire_run, preference_judgment, preference_signal, and self_observation, keep the question focused on the missing action detail and do not downgrade the request into generic batch CRUD.",
|
|
3083
|
-
"For read-model-only health surfaces such as sleep_overview and
|
|
3102
|
+
"For read-model-only health surfaces such as sleep_overview, sports_overview, and training_load, use the dedicated overview reads first when the user wants review, pattern interpretation, recovery context, training-load context, or cardiovascular target analysis. Move to sleep_session or workout_session writes only after one specific stored session needs enrichment.",
|
|
3084
3103
|
"For normal stored Preferences and questionnaire records, use batch CRUD by default; switch to dedicated action routes only for judgments, signals, run answers, clone/draft/publish lifecycle, or visual comparison gameplay.",
|
|
3085
3104
|
"When the user wants to remember a book, article, paper, source, concept, person, conversation, project reference, recurring explanation, or personal manual, consider wiki_page before note or self_observation.",
|
|
3086
3105
|
"For meaning-bearing updates, especially in Psyche, briefly say what feels newly true before you ask for the one structural detail that still changes the save."
|
|
@@ -3373,6 +3392,18 @@ const AGENT_ONBOARDING_ENTITY_CONVERSATION_PLAYBOOKS = [
|
|
|
3373
3392
|
"Move to a workout_session write only when one specific workout needs reflective context, tags, notes, or links."
|
|
3374
3393
|
]
|
|
3375
3394
|
},
|
|
3395
|
+
{
|
|
3396
|
+
focus: "training_load",
|
|
3397
|
+
openingQuestion: "What training decision should the load picture help with right now?",
|
|
3398
|
+
coachingGoal: "Review cardiovascular load, HR zones, acute/chronic stress, high-intensity pressure, VO2max context, and target distribution before suggesting training changes.",
|
|
3399
|
+
askSequence: [
|
|
3400
|
+
"Ask what decision the user wants from the load surface only if it is not already clear.",
|
|
3401
|
+
"Use the dedicated training-load overview before asking the user to reconstruct workout metrics from memory.",
|
|
3402
|
+
"Separate data-quality limits from true physiological interpretation.",
|
|
3403
|
+
"Translate the load pattern into a concrete next training constraint or target.",
|
|
3404
|
+
"Move to a workout_session write only when one specific workout needs reflective context, tags, notes, or links."
|
|
3405
|
+
]
|
|
3406
|
+
},
|
|
3376
3407
|
{
|
|
3377
3408
|
focus: "preference_catalog",
|
|
3378
3409
|
openingQuestion: "What decision or taste question should this catalog help with?",
|
|
@@ -4228,6 +4259,19 @@ const AGENT_ONBOARDING_TOOL_INPUT_CATALOG = [
|
|
|
4228
4259
|
],
|
|
4229
4260
|
example: '{"userIds":["user_operator"]}'
|
|
4230
4261
|
},
|
|
4262
|
+
{
|
|
4263
|
+
toolName: "forge_get_training_load_overview",
|
|
4264
|
+
summary: "Read the cardiovascular training-load surface with acute/chronic load, HR zone distribution, weekly intensity targets, and data-quality flags.",
|
|
4265
|
+
whenToUse: "Use when the operator wants to analyze training stress, zone balance, VO2max context, combat-sport load, or what to optimize next.",
|
|
4266
|
+
inputShape: "{ userIds?: string[] }",
|
|
4267
|
+
requiredFields: [],
|
|
4268
|
+
notes: [
|
|
4269
|
+
"The API path is /api/v1/health/training-load and the UI route is /training-load.",
|
|
4270
|
+
"This is a read-model-only surface. Workout records remain ordinary workout_session entities for batch CRUD.",
|
|
4271
|
+
"Forge uses HRR zone analytics and TRIMP-like internal load from stored workout evidence."
|
|
4272
|
+
],
|
|
4273
|
+
example: '{"userIds":["user_operator"]}'
|
|
4274
|
+
},
|
|
4231
4275
|
{
|
|
4232
4276
|
toolName: "forge_update_sleep_session",
|
|
4233
4277
|
summary: "Patch one sleep session with reflective notes, tags, or linked Forge context.",
|
|
@@ -5011,6 +5055,8 @@ function buildAgentOnboardingPayload(request) {
|
|
|
5011
5055
|
sleep_overview: "/api/v1/health/sleep",
|
|
5012
5056
|
sportsOverview: "/api/v1/health/fitness",
|
|
5013
5057
|
sports_overview: "/api/v1/health/fitness",
|
|
5058
|
+
trainingLoad: "/api/v1/health/training-load",
|
|
5059
|
+
training_load: "/api/v1/health/training-load",
|
|
5014
5060
|
selfObservation: "/api/v1/psyche/self-observation/calendar",
|
|
5015
5061
|
self_observation: "/api/v1/psyche/self-observation/calendar",
|
|
5016
5062
|
calendarOverview: "/api/v1/calendar/overview",
|
|
@@ -5120,6 +5166,7 @@ function buildAgentOnboardingPayload(request) {
|
|
|
5120
5166
|
weeklyReview: "/api/v1/reviews/weekly",
|
|
5121
5167
|
sleepOverview: "/api/v1/health/sleep",
|
|
5122
5168
|
sportsOverview: "/api/v1/health/fitness",
|
|
5169
|
+
trainingLoad: "/api/v1/health/training-load",
|
|
5123
5170
|
lifeForce: "/api/v1/life-force",
|
|
5124
5171
|
lifeForceProfile: "/api/v1/life-force/profile",
|
|
5125
5172
|
lifeForceWeekdayTemplate: "/api/v1/life-force/templates/:weekday",
|
|
@@ -5171,6 +5218,7 @@ function buildAgentOnboardingPayload(request) {
|
|
|
5171
5218
|
"forge_get_psyche_overview",
|
|
5172
5219
|
"forge_get_sleep_overview",
|
|
5173
5220
|
"forge_get_sports_overview",
|
|
5221
|
+
"forge_get_training_load_overview",
|
|
5174
5222
|
"forge_get_xp_metrics",
|
|
5175
5223
|
"forge_get_weekly_review"
|
|
5176
5224
|
],
|
|
@@ -5201,6 +5249,7 @@ function buildAgentOnboardingPayload(request) {
|
|
|
5201
5249
|
healthWorkflow: [
|
|
5202
5250
|
"forge_get_sleep_overview",
|
|
5203
5251
|
"forge_get_sports_overview",
|
|
5252
|
+
"forge_get_training_load_overview",
|
|
5204
5253
|
"forge_update_sleep_session",
|
|
5205
5254
|
"forge_update_workout_session"
|
|
5206
5255
|
],
|
|
@@ -5268,8 +5317,12 @@ function buildAgentOnboardingPayload(request) {
|
|
|
5268
5317
|
movementTripDetail: '{"routeKey":"tripDetail","pathParams":{"id":"trip_123"}}',
|
|
5269
5318
|
movementSettings: '{"routeKey":"settings","query":{"userIds":["user_operator"]}}',
|
|
5270
5319
|
movementSettingsUpdate: '{"routeKey":"settingsUpdate","body":{"trackingEnabled":true,"publishMode":"draft_review","retentionMode":"aggregates_only"}}',
|
|
5320
|
+
movementPlaceCreate: '{"routeKey":"placeCreate","body":{"label":"Home","centerLat":46.2044,"centerLon":6.1432,"radiusMeters":120,"userId":"user_operator","note":"Primary home boundary for future time-in-place reads."}}',
|
|
5321
|
+
movementPlaceUpdate: '{"routeKey":"placeUpdate","pathParams":{"id":"place_home"},"body":{"label":"Home office","radiusMeters":90,"note":"Tighten the boundary so clinic visits do not count as home."}}',
|
|
5271
5322
|
movementMissingStayPreflight: '{"routeKey":"userBoxPreflight","body":{"kind":"stay","startedAt":"2026-05-06T13:00:00.000Z","endedAt":"2026-05-06T15:00:00.000Z","placeLabel":"Home","userId":"user_operator"}}',
|
|
5272
5323
|
movementMissingStayCreate: '{"routeKey":"userBoxCreate","body":{"kind":"stay","startedAt":"2026-05-06T13:00:00.000Z","endedAt":"2026-05-06T15:00:00.000Z","placeLabel":"Home","userId":"user_operator","note":"Manual correction after reviewing the timeline."}}',
|
|
5324
|
+
movementUserBoxUpdate: '{"routeKey":"userBoxUpdate","pathParams":{"id":"box_manual_123"},"body":{"endedAt":"2026-05-06T15:30:00.000Z","note":"Extended after checking the timeline detail."}}',
|
|
5325
|
+
movementUserBoxDelete: '{"routeKey":"userBoxDelete","pathParams":{"id":"box_manual_123"}}',
|
|
5273
5326
|
lifeForceOverview: '{"routeKey":"overview"}',
|
|
5274
5327
|
lifeForceProfile: '{"routeKey":"profile","body":{"baselineDailyAp":24,"recoveryNotes":"Clinic-admin days need a lower expected afternoon load."}}',
|
|
5275
5328
|
lifeForceWeekdayTemplate: '{"routeKey":"weekdayTemplate","pathParams":{"weekday":"monday"},"body":{"points":[{"hour":13,"freeAp":-4}]}}',
|
|
@@ -5280,6 +5333,8 @@ function buildAgentOnboardingPayload(request) {
|
|
|
5280
5333
|
workbenchUpdateFlow: '{"routeKey":"updateFlow","pathParams":{"id":"flow_research_digest"},"body":{"description":"Keep the same input contract but add a stronger evidence-check node."}}',
|
|
5281
5334
|
workbenchDeleteFlow: '{"routeKey":"deleteFlow","pathParams":{"id":"flow_research_digest"}}',
|
|
5282
5335
|
workbenchRunDetail: '{"routeKey":"runDetail","pathParams":{"id":"flow_research_digest","runId":"run_123"}}',
|
|
5336
|
+
workbenchRunNodes: '{"routeKey":"runNodes","pathParams":{"id":"flow_research_digest","runId":"run_123"}}',
|
|
5337
|
+
workbenchNodeResult: '{"routeKey":"nodeResult","pathParams":{"id":"flow_research_digest","runId":"run_123","nodeId":"node_summary"}}',
|
|
5283
5338
|
workbenchPublishedOutput: '{"routeKey":"publishedOutput","pathParams":{"id":"flow_research_digest"}}',
|
|
5284
5339
|
workbenchLatestNodeOutput: '{"routeKey":"latestNodeOutput","pathParams":{"id":"flow_research_digest","nodeId":"node_summary"}}',
|
|
5285
5340
|
workbenchRunFlow: '{"routeKey":"runFlow","pathParams":{"id":"flow_research_digest"},"body":{"input":{"topic":"question flow quality"}}}',
|
|
@@ -6202,6 +6257,16 @@ function compactFitness(fitness) {
|
|
|
6202
6257
|
detailRoute: "/api/v1/health/fitness"
|
|
6203
6258
|
};
|
|
6204
6259
|
}
|
|
6260
|
+
function compactTrainingLoad(trainingLoad) {
|
|
6261
|
+
return {
|
|
6262
|
+
summary: trainingLoad.summary,
|
|
6263
|
+
intensityDistribution: trainingLoad.recentIntensityDistribution,
|
|
6264
|
+
weeklyLoad: trainingLoad.weeklyLoad.slice(-8),
|
|
6265
|
+
topActivities: trainingLoad.activityBreakdown.slice(0, 6),
|
|
6266
|
+
targetModel: trainingLoad.targetModel,
|
|
6267
|
+
detailRoute: "/api/v1/health/training-load"
|
|
6268
|
+
};
|
|
6269
|
+
}
|
|
6205
6270
|
function compactVitals(vitals) {
|
|
6206
6271
|
return {
|
|
6207
6272
|
summary: vitals.summary,
|
|
@@ -6482,6 +6547,7 @@ function buildOperatorOverview(request) {
|
|
|
6482
6547
|
const yesterday = compactDailyContext(getTodayContext(addDays(now, -1), { userIds }));
|
|
6483
6548
|
const sleep = compactSleep(getSleepViewData(userIds));
|
|
6484
6549
|
const fitness = compactFitness(getFitnessViewData(userIds));
|
|
6550
|
+
const trainingLoad = compactTrainingLoad(getTrainingLoadViewData(userIds));
|
|
6485
6551
|
const vitals = compactVitals(getVitalsViewData(userIds));
|
|
6486
6552
|
const lifeForce = compactLifeForce(buildLifeForcePayload(now, userIds));
|
|
6487
6553
|
const psyche = canReadPsyche ? compactPsyche(getPsycheOverview(userIds)) : null;
|
|
@@ -6563,6 +6629,7 @@ function buildOperatorOverview(request) {
|
|
|
6563
6629
|
notes,
|
|
6564
6630
|
sleep,
|
|
6565
6631
|
fitness,
|
|
6632
|
+
trainingLoad,
|
|
6566
6633
|
vitals,
|
|
6567
6634
|
lifeForce,
|
|
6568
6635
|
domains: listDomains().map((domain) => ({
|
|
@@ -7241,6 +7308,9 @@ export async function buildServer(options = {}) {
|
|
|
7241
7308
|
app.get("/api/v1/health/fitness", async (request) => ({
|
|
7242
7309
|
fitness: getFitnessViewData(resolveScopedUserIds(request.query))
|
|
7243
7310
|
}));
|
|
7311
|
+
app.get("/api/v1/health/training-load", async (request) => ({
|
|
7312
|
+
trainingLoad: getTrainingLoadViewData(resolveScopedUserIds(request.query))
|
|
7313
|
+
}));
|
|
7244
7314
|
app.get("/api/v1/health/vitals", async (request) => ({
|
|
7245
7315
|
vitals: getVitalsViewData(resolveScopedUserIds(request.query))
|
|
7246
7316
|
}));
|
|
@@ -365,9 +365,15 @@ export function upsertWorkoutTimeSeries(input) {
|
|
|
365
365
|
quality_flags_json, metadata_json, provenance_json, created_at, updated_at
|
|
366
366
|
)
|
|
367
367
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
368
|
-
ON CONFLICT(workout_id, metric_key, source_sample_uid
|
|
369
|
-
DO UPDATE SET
|
|
368
|
+
ON CONFLICT(workout_id, metric_key, source_sample_uid)
|
|
369
|
+
DO UPDATE SET series_index = excluded.series_index,
|
|
370
|
+
value = excluded.value, started_at = excluded.started_at,
|
|
370
371
|
ended_at = excluded.ended_at, source_device = excluded.source_device,
|
|
372
|
+
label = excluded.label,
|
|
373
|
+
category = excluded.category,
|
|
374
|
+
unit = excluded.unit,
|
|
375
|
+
source_bundle_identifier = excluded.source_bundle_identifier,
|
|
376
|
+
source_product_type = excluded.source_product_type,
|
|
371
377
|
capture_method = excluded.capture_method,
|
|
372
378
|
quality_flags_json = excluded.quality_flags_json,
|
|
373
379
|
metadata_json = excluded.metadata_json,
|