forge-openclaw-plugin 0.2.47 → 0.2.49

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.
Files changed (32) hide show
  1. package/README.md +6 -5
  2. package/dist/assets/index-2_tuemtU.css +1 -0
  3. package/dist/assets/index-BAmEvOXb.js +91 -0
  4. package/dist/assets/index-BAmEvOXb.js.map +1 -0
  5. package/dist/index.html +2 -2
  6. package/dist/openclaw/api-client.js +15 -1
  7. package/dist/openclaw/session-registry.js +17 -0
  8. package/dist/openclaw/tools.js +1 -1
  9. package/dist/server/server/migrations/052_agent_identity_tightening.sql +307 -0
  10. package/dist/server/server/migrations/053_agent_runtime_session_canonical_labels.sql +9 -0
  11. package/dist/server/server/src/app.js +42 -12
  12. package/dist/server/server/src/health-workout-adapters.js +465 -0
  13. package/dist/server/server/src/health.js +134 -9
  14. package/dist/server/server/src/openapi.js +33 -0
  15. package/dist/server/server/src/repositories/agent-runtime-sessions.js +122 -16
  16. package/dist/server/server/src/repositories/habits.js +62 -25
  17. package/dist/server/server/src/repositories/model-settings.js +5 -0
  18. package/dist/server/server/src/repositories/settings.js +101 -13
  19. package/dist/server/server/src/repositories/users.js +23 -0
  20. package/dist/server/server/src/types.js +22 -6
  21. package/dist/server/server/src/watch-mobile.js +33 -21
  22. package/dist/server/src/lib/date-keys.js +21 -0
  23. package/openclaw.plugin.json +1 -1
  24. package/package.json +5 -2
  25. package/server/migrations/052_agent_identity_tightening.sql +307 -0
  26. package/server/migrations/053_agent_runtime_session_canonical_labels.sql +9 -0
  27. package/skills/forge-openclaw/SKILL.md +3 -1
  28. package/skills/forge-openclaw/entity_conversation_playbooks.md +45 -8
  29. package/skills/forge-openclaw/psyche_entity_playbooks.md +14 -0
  30. package/dist/assets/index-BejDHw1R.js +0 -91
  31. package/dist/assets/index-BejDHw1R.js.map +0 -1
  32. package/dist/assets/index-DtEvFzXp.css +0 -1
@@ -0,0 +1,465 @@
1
+ import { z } from "zod";
2
+ const scalarSchema = z.union([z.string(), z.number(), z.boolean(), z.null()]);
3
+ export const workoutActivityDescriptorSchema = z.object({
4
+ sourceSystem: z.string().trim().min(1),
5
+ providerActivityType: z.string().trim().min(1),
6
+ providerRawValue: z.number().int().nullable().optional(),
7
+ canonicalKey: z.string().trim().min(1),
8
+ canonicalLabel: z.string().trim().min(1),
9
+ familyKey: z.string().trim().min(1),
10
+ familyLabel: z.string().trim().min(1),
11
+ isFallback: z.boolean().default(false)
12
+ });
13
+ export const workoutMetricSchema = z.object({
14
+ key: z.string().trim().min(1),
15
+ label: z.string().trim().min(1),
16
+ category: z.string().trim().min(1),
17
+ unit: z.string().trim().min(1).default("count"),
18
+ statistic: z.string().trim().min(1).default("value"),
19
+ value: scalarSchema,
20
+ startedAt: z.string().datetime().nullable().optional(),
21
+ endedAt: z.string().datetime().nullable().optional()
22
+ });
23
+ export const workoutEventSchema = z.object({
24
+ type: z.string().trim().min(1),
25
+ label: z.string().trim().min(1),
26
+ startedAt: z.string().datetime(),
27
+ endedAt: z.string().datetime().nullable().optional(),
28
+ durationSeconds: z.number().int().nonnegative().default(0),
29
+ metadata: z.record(z.string(), scalarSchema).default({})
30
+ });
31
+ export const workoutComponentSchema = z.object({
32
+ externalUid: z.string().trim().min(1),
33
+ startedAt: z.string().datetime(),
34
+ endedAt: z.string().datetime().nullable().optional(),
35
+ durationSeconds: z.number().int().nonnegative().default(0),
36
+ activity: workoutActivityDescriptorSchema,
37
+ metrics: z.array(workoutMetricSchema).default([]),
38
+ metadata: z.record(z.string(), scalarSchema).default({})
39
+ });
40
+ export const workoutDetailsSchema = z.object({
41
+ sourceSystem: z.string().trim().min(1),
42
+ metrics: z.array(workoutMetricSchema).default([]),
43
+ events: z.array(workoutEventSchema).default([]),
44
+ components: z.array(workoutComponentSchema).default([]),
45
+ metadata: z.record(z.string(), scalarSchema).default({})
46
+ });
47
+ const APPLE_HEALTH_ACTIVITY_TYPES = new Map([
48
+ [1, { key: "american_football", label: "American football" }],
49
+ [2, { key: "archery", label: "Archery" }],
50
+ [3, { key: "australian_football", label: "Australian football" }],
51
+ [4, { key: "badminton", label: "Badminton" }],
52
+ [5, { key: "baseball", label: "Baseball" }],
53
+ [6, { key: "basketball", label: "Basketball" }],
54
+ [7, { key: "bowling", label: "Bowling" }],
55
+ [8, { key: "boxing", label: "Boxing" }],
56
+ [9, { key: "climbing", label: "Climbing" }],
57
+ [10, { key: "cricket", label: "Cricket" }],
58
+ [11, { key: "cross_training", label: "Cross training" }],
59
+ [12, { key: "curling", label: "Curling" }],
60
+ [13, { key: "cycling", label: "Cycling" }],
61
+ [14, { key: "dance", label: "Dance" }],
62
+ [15, { key: "dance_inspired_training", label: "Dance-inspired training" }],
63
+ [16, { key: "elliptical", label: "Elliptical" }],
64
+ [17, { key: "equestrian_sports", label: "Equestrian sports" }],
65
+ [18, { key: "fencing", label: "Fencing" }],
66
+ [19, { key: "fishing", label: "Fishing" }],
67
+ [20, { key: "functional_strength_training", label: "Functional strength training" }],
68
+ [21, { key: "golf", label: "Golf" }],
69
+ [22, { key: "gymnastics", label: "Gymnastics" }],
70
+ [23, { key: "handball", label: "Handball" }],
71
+ [24, { key: "hiking", label: "Hiking" }],
72
+ [25, { key: "hockey", label: "Hockey" }],
73
+ [26, { key: "hunting", label: "Hunting" }],
74
+ [27, { key: "lacrosse", label: "Lacrosse" }],
75
+ [28, { key: "martial_arts", label: "Martial arts" }],
76
+ [29, { key: "mind_and_body", label: "Mind and body" }],
77
+ [30, { key: "mixed_metabolic_cardio_training", label: "Mixed metabolic cardio training" }],
78
+ [31, { key: "paddle_sports", label: "Paddle sports" }],
79
+ [32, { key: "play", label: "Play" }],
80
+ [33, { key: "preparation_and_recovery", label: "Preparation and recovery" }],
81
+ [34, { key: "racquetball", label: "Racquetball" }],
82
+ [35, { key: "rowing", label: "Rowing" }],
83
+ [36, { key: "rugby", label: "Rugby" }],
84
+ [37, { key: "running", label: "Running" }],
85
+ [38, { key: "sailing", label: "Sailing" }],
86
+ [39, { key: "skating_sports", label: "Skating sports" }],
87
+ [40, { key: "snow_sports", label: "Snow sports" }],
88
+ [41, { key: "soccer", label: "Soccer" }],
89
+ [42, { key: "softball", label: "Softball" }],
90
+ [43, { key: "squash", label: "Squash" }],
91
+ [44, { key: "stair_climbing", label: "Stair climbing" }],
92
+ [45, { key: "surfing_sports", label: "Surfing sports" }],
93
+ [46, { key: "swimming", label: "Swimming" }],
94
+ [47, { key: "table_tennis", label: "Table tennis" }],
95
+ [48, { key: "tennis", label: "Tennis" }],
96
+ [49, { key: "track_and_field", label: "Track and field" }],
97
+ [50, { key: "traditional_strength_training", label: "Traditional strength training" }],
98
+ [51, { key: "volleyball", label: "Volleyball" }],
99
+ [52, { key: "walking", label: "Walking" }],
100
+ [53, { key: "water_fitness", label: "Water fitness" }],
101
+ [54, { key: "water_polo", label: "Water polo" }],
102
+ [55, { key: "water_sports", label: "Water sports" }],
103
+ [56, { key: "wrestling", label: "Wrestling" }],
104
+ [57, { key: "yoga", label: "Yoga" }],
105
+ [58, { key: "barre", label: "Barre" }],
106
+ [59, { key: "core_training", label: "Core training" }],
107
+ [60, { key: "cross_country_skiing", label: "Cross-country skiing" }],
108
+ [61, { key: "downhill_skiing", label: "Downhill skiing" }],
109
+ [62, { key: "flexibility", label: "Flexibility" }],
110
+ [63, { key: "high_intensity_interval_training", label: "High-intensity interval training" }],
111
+ [64, { key: "jump_rope", label: "Jump rope" }],
112
+ [65, { key: "kickboxing", label: "Kickboxing" }],
113
+ [66, { key: "pilates", label: "Pilates" }],
114
+ [67, { key: "snowboarding", label: "Snowboarding" }],
115
+ [68, { key: "stairs", label: "Stairs" }],
116
+ [69, { key: "step_training", label: "Step training" }],
117
+ [70, { key: "wheelchair_walk_pace", label: "Wheelchair walk pace" }],
118
+ [71, { key: "wheelchair_run_pace", label: "Wheelchair run pace" }],
119
+ [72, { key: "tai_chi", label: "Tai chi" }],
120
+ [73, { key: "mixed_cardio", label: "Mixed cardio" }],
121
+ [74, { key: "hand_cycling", label: "Hand cycling" }],
122
+ [75, { key: "disc_sports", label: "Disc sports" }],
123
+ [76, { key: "fitness_gaming", label: "Fitness gaming" }],
124
+ [77, { key: "cardio_dance", label: "Cardio dance" }],
125
+ [78, { key: "social_dance", label: "Social dance" }],
126
+ [79, { key: "pickleball", label: "Pickleball" }],
127
+ [80, { key: "cooldown", label: "Cooldown" }],
128
+ [82, { key: "swim_bike_run", label: "Swim-bike-run" }],
129
+ [83, { key: "transition", label: "Transition" }],
130
+ [84, { key: "underwater_diving", label: "Underwater diving" }],
131
+ [3000, { key: "other", label: "Other" }]
132
+ ]);
133
+ const CARDIO_KEYS = new Set([
134
+ "walking",
135
+ "running",
136
+ "cycling",
137
+ "rowing",
138
+ "elliptical",
139
+ "hiking",
140
+ "mixed_cardio",
141
+ "mixed_metabolic_cardio_training",
142
+ "high_intensity_interval_training",
143
+ "jump_rope",
144
+ "stair_climbing",
145
+ "stairs",
146
+ "step_training",
147
+ "cross_country_skiing",
148
+ "downhill_skiing",
149
+ "snowboarding",
150
+ "hand_cycling",
151
+ "wheelchair_walk_pace",
152
+ "wheelchair_run_pace",
153
+ "track_and_field",
154
+ "cross_training",
155
+ "cardio_dance",
156
+ "fitness_gaming",
157
+ "swim_bike_run",
158
+ "transition"
159
+ ]);
160
+ const STRENGTH_KEYS = new Set([
161
+ "traditional_strength_training",
162
+ "functional_strength_training",
163
+ "core_training",
164
+ "cross_training",
165
+ "climbing"
166
+ ]);
167
+ const MOBILITY_KEYS = new Set([
168
+ "barre",
169
+ "pilates",
170
+ "flexibility",
171
+ "preparation_and_recovery",
172
+ "cooldown"
173
+ ]);
174
+ const MINDFUL_KEYS = new Set([
175
+ "mind_and_body",
176
+ "yoga",
177
+ "tai_chi"
178
+ ]);
179
+ const WATER_KEYS = new Set([
180
+ "swimming",
181
+ "water_fitness",
182
+ "water_polo",
183
+ "water_sports",
184
+ "paddle_sports",
185
+ "surfing_sports",
186
+ "sailing",
187
+ "underwater_diving"
188
+ ]);
189
+ const TEAM_SPORT_KEYS = new Set([
190
+ "american_football",
191
+ "australian_football",
192
+ "baseball",
193
+ "basketball",
194
+ "cricket",
195
+ "handball",
196
+ "hockey",
197
+ "lacrosse",
198
+ "rugby",
199
+ "soccer",
200
+ "softball",
201
+ "volleyball",
202
+ "water_polo"
203
+ ]);
204
+ const RACKET_KEYS = new Set([
205
+ "badminton",
206
+ "pickleball",
207
+ "racquetball",
208
+ "squash",
209
+ "table_tennis",
210
+ "tennis"
211
+ ]);
212
+ const COMBAT_KEYS = new Set([
213
+ "boxing",
214
+ "kickboxing",
215
+ "martial_arts",
216
+ "wrestling",
217
+ "fencing"
218
+ ]);
219
+ const WINTER_KEYS = new Set([
220
+ "cross_country_skiing",
221
+ "downhill_skiing",
222
+ "snow_sports",
223
+ "snowboarding",
224
+ "curling"
225
+ ]);
226
+ function cleanString(value) {
227
+ return typeof value === "string" && value.trim().length > 0
228
+ ? value.trim()
229
+ : null;
230
+ }
231
+ function humanizeKey(value) {
232
+ return value
233
+ .trim()
234
+ .replace(/^activity_/i, "")
235
+ .replaceAll("_", " ")
236
+ .replace(/\s+/g, " ")
237
+ .replace(/\b\w/g, (letter) => letter.toUpperCase());
238
+ }
239
+ function normalizeCanonicalKey(value) {
240
+ return value
241
+ .trim()
242
+ .toLowerCase()
243
+ .replaceAll("-", "_")
244
+ .replace(/\s+/g, "_");
245
+ }
246
+ function resolveActivityFamily(key) {
247
+ const normalized = normalizeCanonicalKey(key);
248
+ if (CARDIO_KEYS.has(normalized)) {
249
+ return { familyKey: "cardio", familyLabel: "Cardio" };
250
+ }
251
+ if (STRENGTH_KEYS.has(normalized)) {
252
+ return { familyKey: "strength", familyLabel: "Strength" };
253
+ }
254
+ if (MOBILITY_KEYS.has(normalized)) {
255
+ return { familyKey: "mobility", familyLabel: "Mobility" };
256
+ }
257
+ if (MINDFUL_KEYS.has(normalized)) {
258
+ return { familyKey: "mindful", familyLabel: "Mindful" };
259
+ }
260
+ if (WATER_KEYS.has(normalized)) {
261
+ return { familyKey: "water", familyLabel: "Water" };
262
+ }
263
+ if (TEAM_SPORT_KEYS.has(normalized)) {
264
+ return { familyKey: "team_sport", familyLabel: "Team sport" };
265
+ }
266
+ if (RACKET_KEYS.has(normalized)) {
267
+ return { familyKey: "racket", familyLabel: "Racket" };
268
+ }
269
+ if (COMBAT_KEYS.has(normalized)) {
270
+ return { familyKey: "combat", familyLabel: "Combat" };
271
+ }
272
+ if (WINTER_KEYS.has(normalized)) {
273
+ return { familyKey: "winter", familyLabel: "Winter" };
274
+ }
275
+ if (normalized.includes("dance") ||
276
+ normalized === "play" ||
277
+ normalized === "disc_sports" ||
278
+ normalized === "golf" ||
279
+ normalized === "gymnastics" ||
280
+ normalized === "bowling") {
281
+ return { familyKey: "recreation", familyLabel: "Recreation" };
282
+ }
283
+ return { familyKey: "other", familyLabel: "Other" };
284
+ }
285
+ function inferSourceSystem(source, sourceType, provenance) {
286
+ const fromProvenance = cleanString(provenance?.sourceSystem);
287
+ if (fromProvenance) {
288
+ return fromProvenance;
289
+ }
290
+ if (source === "apple_health" || sourceType.includes("healthkit")) {
291
+ return "apple_health";
292
+ }
293
+ if (source === "forge_habit" || source === "manual" || sourceType === "manual") {
294
+ return "forge";
295
+ }
296
+ return cleanString(sourceType) ?? cleanString(source) ?? "unknown";
297
+ }
298
+ function buildFallbackActivity(sourceSystem, workoutType, providerActivityType = "generic_workout_type", providerRawValue = null, isFallback = false) {
299
+ const canonicalKey = normalizeCanonicalKey(workoutType.length > 0 ? workoutType : "workout");
300
+ const canonicalLabel = humanizeKey(canonicalKey);
301
+ const family = resolveActivityFamily(canonicalKey);
302
+ return {
303
+ sourceSystem,
304
+ providerActivityType,
305
+ providerRawValue,
306
+ canonicalKey,
307
+ canonicalLabel,
308
+ familyKey: family.familyKey,
309
+ familyLabel: family.familyLabel,
310
+ isFallback
311
+ };
312
+ }
313
+ function normalizeAppleHealthActivity(workoutType, existingActivity) {
314
+ if (existingActivity?.sourceSystem === "apple_health") {
315
+ const family = resolveActivityFamily(existingActivity.canonicalKey);
316
+ return {
317
+ ...existingActivity,
318
+ familyKey: family.familyKey,
319
+ familyLabel: family.familyLabel
320
+ };
321
+ }
322
+ const rawMatch = /^activity_(\d+)$/i.exec(workoutType.trim());
323
+ const rawValue = rawMatch ? Number(rawMatch[1]) : null;
324
+ if (rawValue != null) {
325
+ const catalog = APPLE_HEALTH_ACTIVITY_TYPES.get(rawValue);
326
+ if (catalog) {
327
+ const family = resolveActivityFamily(catalog.key);
328
+ return {
329
+ sourceSystem: "apple_health",
330
+ providerActivityType: "hk_workout_activity_type",
331
+ providerRawValue: rawValue,
332
+ canonicalKey: catalog.key,
333
+ canonicalLabel: catalog.label,
334
+ familyKey: family.familyKey,
335
+ familyLabel: family.familyLabel,
336
+ isFallback: false
337
+ };
338
+ }
339
+ }
340
+ const normalizedKey = normalizeCanonicalKey(workoutType);
341
+ for (const [providerRawValue, catalog] of APPLE_HEALTH_ACTIVITY_TYPES.entries()) {
342
+ if (catalog.key === normalizedKey) {
343
+ const family = resolveActivityFamily(catalog.key);
344
+ return {
345
+ sourceSystem: "apple_health",
346
+ providerActivityType: "hk_workout_activity_type",
347
+ providerRawValue,
348
+ canonicalKey: catalog.key,
349
+ canonicalLabel: catalog.label,
350
+ familyKey: family.familyKey,
351
+ familyLabel: family.familyLabel,
352
+ isFallback: false
353
+ };
354
+ }
355
+ }
356
+ return buildFallbackActivity("apple_health", normalizedKey, "hk_workout_activity_type", rawValue, true);
357
+ }
358
+ const workoutSourceAdapters = new Map([
359
+ [
360
+ "apple_health",
361
+ {
362
+ sourceSystem: "apple_health",
363
+ normalizeActivity: ({ workoutType, existingActivity }) => normalizeAppleHealthActivity(workoutType, existingActivity)
364
+ }
365
+ ],
366
+ [
367
+ "forge",
368
+ {
369
+ sourceSystem: "forge",
370
+ normalizeActivity: ({ workoutType, existingActivity }) => {
371
+ if (existingActivity) {
372
+ return {
373
+ ...existingActivity,
374
+ ...resolveActivityFamily(existingActivity.canonicalKey)
375
+ };
376
+ }
377
+ return buildFallbackActivity("forge", workoutType, "forge_workout_type");
378
+ }
379
+ }
380
+ ]
381
+ ]);
382
+ function getWorkoutSourceAdapter(sourceSystem) {
383
+ return (workoutSourceAdapters.get(sourceSystem) ??
384
+ {
385
+ sourceSystem,
386
+ normalizeActivity: ({ workoutType, existingActivity }) => {
387
+ if (existingActivity) {
388
+ return {
389
+ ...existingActivity,
390
+ ...resolveActivityFamily(existingActivity.canonicalKey)
391
+ };
392
+ }
393
+ return buildFallbackActivity(sourceSystem, workoutType, "generic_workout_type");
394
+ }
395
+ });
396
+ }
397
+ function normalizeWorkoutDetails(sourceSystem, value) {
398
+ const parsed = workoutDetailsSchema.safeParse(value);
399
+ if (!parsed.success) {
400
+ return {
401
+ sourceSystem,
402
+ metrics: [],
403
+ events: [],
404
+ components: [],
405
+ metadata: {}
406
+ };
407
+ }
408
+ return {
409
+ ...parsed.data,
410
+ sourceSystem,
411
+ metrics: [...parsed.data.metrics].sort((left, right) => {
412
+ if (left.category === right.category) {
413
+ return left.label.localeCompare(right.label);
414
+ }
415
+ return left.category.localeCompare(right.category);
416
+ }),
417
+ events: [...parsed.data.events].sort((left, right) => left.startedAt.localeCompare(right.startedAt)),
418
+ components: [...parsed.data.components].sort((left, right) => left.startedAt.localeCompare(right.startedAt))
419
+ };
420
+ }
421
+ export function buildWorkoutSessionPresentation(input) {
422
+ const provenance = input.provenance ?? {};
423
+ const derived = input.derived ?? {};
424
+ const sourceSystem = inferSourceSystem(input.source, input.sourceType, provenance);
425
+ const storedActivity = workoutActivityDescriptorSchema.safeParse(derived.activity).success
426
+ ? workoutActivityDescriptorSchema.parse(derived.activity)
427
+ : workoutActivityDescriptorSchema.safeParse(provenance.activity).success
428
+ ? workoutActivityDescriptorSchema.parse(provenance.activity)
429
+ : null;
430
+ const adapter = getWorkoutSourceAdapter(sourceSystem);
431
+ const activity = adapter.normalizeActivity({
432
+ workoutType: input.workoutType,
433
+ existingActivity: storedActivity
434
+ });
435
+ const details = normalizeWorkoutDetails(sourceSystem, derived.details ?? provenance.details);
436
+ return {
437
+ sourceSystem,
438
+ sourceBundleIdentifier: cleanString(provenance.sourceBundleIdentifier),
439
+ sourceProductType: cleanString(provenance.sourceProductType),
440
+ workoutType: activity.canonicalKey,
441
+ workoutTypeLabel: activity.canonicalLabel,
442
+ activityFamily: activity.familyKey,
443
+ activityFamilyLabel: activity.familyLabel,
444
+ activity,
445
+ details
446
+ };
447
+ }
448
+ export function buildWorkoutSessionPersistenceSeed(input) {
449
+ const sourceSystem = cleanString(input.sourceSystem) ??
450
+ inferSourceSystem(input.source, input.sourceType, undefined);
451
+ const parsedActivity = workoutActivityDescriptorSchema.safeParse(input.activity);
452
+ const adapter = getWorkoutSourceAdapter(sourceSystem);
453
+ const activity = adapter.normalizeActivity({
454
+ workoutType: input.workoutType,
455
+ existingActivity: parsedActivity.success ? parsedActivity.data : null
456
+ });
457
+ const details = normalizeWorkoutDetails(sourceSystem, input.details);
458
+ return {
459
+ sourceSystem,
460
+ sourceBundleIdentifier: cleanString(input.sourceBundleIdentifier),
461
+ sourceProductType: cleanString(input.sourceProductType),
462
+ activity,
463
+ details
464
+ };
465
+ }