forge-openclaw-plugin 0.2.102 → 0.2.104

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 (175) hide show
  1. package/dist/assets/activity-page-Dv6X5ZCV.js +1 -0
  2. package/dist/assets/{ai-surface-workspace-xtB5RFQu.js → ai-surface-workspace-CutiG6uS.js} +1 -1
  3. package/dist/assets/{atlas-panel-B3dPHCmZ.js → atlas-panel-jgMRyaHn.js} +1 -1
  4. package/dist/assets/{board-DqHzdCPQ.js → board-dIX6etHh.js} +1 -1
  5. package/dist/assets/calendar-page-q7Nm5E2U.js +1 -0
  6. package/dist/assets/calendar-rules-DPFsfiRl.js +1 -0
  7. package/dist/assets/{calendar-week-toolbar-BZ_-X3Wb.js → calendar-week-toolbar-nVhgB-0s.js} +1 -1
  8. package/dist/assets/{charts-FcU0F3XV.js → charts-DFnEuIMB.js} +1 -1
  9. package/dist/assets/companion-sync-lab-page-CJ8UTij8.js +1 -0
  10. package/dist/assets/{daily-metrics-dashboard-BMyL0Qr4.js → daily-metrics-dashboard-CiJkkkd1.js} +1 -1
  11. package/dist/assets/entity-note-count-link-RzBB6ujx.js +1 -0
  12. package/dist/assets/{entity-notes-surface-CBylYDwy.js → entity-notes-surface-Djz50HvD.js} +1 -1
  13. package/dist/assets/execution-board-CtPhI-58.js +1 -0
  14. package/dist/assets/{faceted-token-search-D7xPWfOl.js → faceted-token-search-DEC6ANa9.js} +1 -1
  15. package/dist/assets/{flagship-signal-deck-BEFKOhvx.js → flagship-signal-deck-CtHC3mql.js} +1 -1
  16. package/dist/assets/floating-action-menu-CKQRhff9.js +1 -0
  17. package/dist/assets/{forms-CmLAyGqz.js → forms-hB0SqEh-.js} +1 -1
  18. package/dist/assets/goal-detail-page-CF5fNQeR.js +1 -0
  19. package/dist/assets/goals-page-fsY8NzGB.js +1 -0
  20. package/dist/assets/{graph-BTa79qum.js → graph-DDUZNRsO.js} +1 -1
  21. package/dist/assets/habits-page-COoGz4kj.js +1 -0
  22. package/dist/assets/index-BAXYM89v.css +1 -0
  23. package/dist/assets/index-EqQsXoat.js +19 -0
  24. package/dist/assets/{insight-flow-dialog-BtIQXXsS.js → insight-flow-dialog-DtX_5W7g.js} +1 -1
  25. package/dist/assets/insights-page-cBd74ObU.js +8 -0
  26. package/dist/assets/kanban-page-CYav9THw.js +1 -0
  27. package/dist/assets/knowledge-graph-page-D2A-W1jE.js +1 -0
  28. package/dist/assets/{life-force-page-BGDkKfbJ.js → life-force-page-CACGlNhq.js} +1 -1
  29. package/dist/assets/life-force-workspace-BJ4N1I78.js +1 -0
  30. package/dist/assets/{maps-CF1RagUX.js → maps-C-yOWiDN.js} +1 -1
  31. package/dist/assets/{metric-tile-4iMd_WnJ.js → metric-tile-CMadwnGz.js} +1 -1
  32. package/dist/assets/{motion-CfdU2F35.js → motion-Lt5B1XuE.js} +1 -1
  33. package/dist/assets/movement-page-D5VqFd2q.js +1 -0
  34. package/dist/assets/{note-markdown-CsGQhQXF.js → note-markdown-BzK2Qlgr.js} +1 -1
  35. package/dist/assets/{note-tags-input-DdZi93tj.js → note-tags-input-CZzqJMLc.js} +1 -1
  36. package/dist/assets/notes-page-D3Hsh90C.js +1 -0
  37. package/dist/assets/{open-in-graph-button-C0TGev7c.js → open-in-graph-button-BFPKfyK3.js} +1 -1
  38. package/dist/assets/{orbit-map-DVXkfQdd.js → orbit-map-CnyfSmOG.js} +1 -1
  39. package/dist/assets/{overview-page-BwMlFQKX.js → overview-page-CXdWrOV1.js} +1 -1
  40. package/dist/assets/{page-hero-oFHaAjtL.js → page-hero-ffKzgyW3.js} +1 -1
  41. package/dist/assets/pill-cluster-C2D0h3lx.js +1 -0
  42. package/dist/assets/{preference-entity-handoff-button-DOKV9bZc.js → preference-entity-handoff-button-Dj3V6VxL.js} +1 -1
  43. package/dist/assets/preferences-page-Jo8Gw386.js +1 -0
  44. package/dist/assets/project-collections-qSqp90HN.js +1 -0
  45. package/dist/assets/{project-detail-page-DogvVNEn.js → project-detail-page-BifhiLQX.js} +1 -1
  46. package/dist/assets/project-management-hierarchy-page-DZ_9klIc.js +1 -0
  47. package/dist/assets/project-management-section-nav-BImLCVvf.js +1 -0
  48. package/dist/assets/projects-page-ptcx6H38.js +1 -0
  49. package/dist/assets/{psyche-behaviors-page-BeVlXvbj.js → psyche-behaviors-page-BDwXyDta.js} +1 -1
  50. package/dist/assets/{psyche-flashcards-page-CqAWCO4E.js → psyche-flashcards-page-DL1BP1jX.js} +1 -1
  51. package/dist/assets/{psyche-goal-map-page-IyNnbk_W.js → psyche-goal-map-page-ovcpisx1.js} +1 -1
  52. package/dist/assets/{psyche-graph-IkUQRaDK.js → psyche-graph-EP5GL612.js} +1 -1
  53. package/dist/assets/{psyche-metrics-page-uai0a7Lx.js → psyche-metrics-page-Bcu813Rg.js} +1 -1
  54. package/dist/assets/{psyche-mode-guide-page-C7p-ABiF.js → psyche-mode-guide-page-C0g27Xpt.js} +1 -1
  55. package/dist/assets/{psyche-modes-page-CLQ5V3w0.js → psyche-modes-page-BjKjX5MR.js} +1 -1
  56. package/dist/assets/{psyche-page-NQBHkkpU.js → psyche-page-F4tF2W70.js} +1 -1
  57. package/dist/assets/{psyche-patterns-page-BkRiNpI_.js → psyche-patterns-page-B-14hukK.js} +1 -1
  58. package/dist/assets/{psyche-questionnaire-builder-page-Du-7HwPC.js → psyche-questionnaire-builder-page-BdXmoHvK.js} +1 -1
  59. package/dist/assets/{psyche-questionnaire-detail-page-C5-yf8MF.js → psyche-questionnaire-detail-page-Cu5uwlJu.js} +1 -1
  60. package/dist/assets/{psyche-questionnaire-run-detail-page-CoSH5O6R.js → psyche-questionnaire-run-detail-page-C3R4PClg.js} +1 -1
  61. package/dist/assets/{psyche-questionnaire-run-page-ChI7c1wy.js → psyche-questionnaire-run-page-Div3iDdt.js} +1 -1
  62. package/dist/assets/{psyche-questionnaires-page-xUcJJzbI.js → psyche-questionnaires-page-DpqAPQCp.js} +1 -1
  63. package/dist/assets/{psyche-report-detail-page-BPrZCFZS.js → psyche-report-detail-page-BvWVDKP3.js} +1 -1
  64. package/dist/assets/{psyche-reports-page-D2EXJmm6.js → psyche-reports-page-BrbWUlAq.js} +1 -1
  65. package/dist/assets/{psyche-schemas-RcZYaokx.js → psyche-schemas-Dxj554nU.js} +1 -1
  66. package/dist/assets/{psyche-schemas-beliefs-page-h0ooZ1lp.js → psyche-schemas-beliefs-page-DYKvAtSD.js} +1 -1
  67. package/dist/assets/{psyche-screen-time-page-_3-4LikV.js → psyche-screen-time-page-CAOKCyQw.js} +1 -1
  68. package/dist/assets/{psyche-self-observation-page-BdbCsgR5.js → psyche-self-observation-page-F0MVA0UH.js} +1 -1
  69. package/dist/assets/{psyche-values-page-DuUVki5e.js → psyche-values-page-DyvX-d0o.js} +1 -1
  70. package/dist/assets/{report-chain-fields-BBMz0sGI.js → report-chain-fields-CeC1cJFS.js} +1 -1
  71. package/dist/assets/rewards-page-C2loyODo.js +1 -0
  72. package/dist/assets/scheduling-rules-editor-D40AC2jR.js +1 -0
  73. package/dist/assets/{schema-badge-D93RcG36.js → schema-badge-FY7818qB.js} +1 -1
  74. package/dist/assets/{select-menu-Cdq7foRu.js → select-menu-DJsCG6rM.js} +1 -1
  75. package/dist/assets/settings-agents-page-BFkJ5AAD.js +6 -0
  76. package/dist/assets/settings-bin-page-D3-Ab-iA.js +1 -0
  77. package/dist/assets/settings-calendar-page-ly_mSTAD.js +5 -0
  78. package/dist/assets/settings-data-page-D7QOqNf-.js +1 -0
  79. package/dist/assets/settings-logs-page-Ca87HEUx.js +1 -0
  80. package/dist/assets/settings-mobile-page-D0jejZot.js +1 -0
  81. package/dist/assets/settings-models-page-DPDsZjSc.js +1 -0
  82. package/dist/assets/settings-page-1cArlSPM.js +1 -0
  83. package/dist/assets/settings-rewards-page-Db4BV1DC.js +1 -0
  84. package/dist/assets/{settings-section-nav-Dc4IeVBt.js → settings-section-nav-DP9o4peU.js} +1 -1
  85. package/dist/assets/settings-users-page-BHFyDSsd.js +1 -0
  86. package/dist/assets/settings-wiki-page-BNaiupBm.js +1 -0
  87. package/dist/assets/sleep-page-BmOPF0yD.js +1 -0
  88. package/dist/assets/sports-page-BldIiclr.js +1 -0
  89. package/dist/assets/{state-VYvD1QVP.js → state-vCcAT5Hq.js} +1 -1
  90. package/dist/assets/strategies-page-CaTN99qj.js +1 -0
  91. package/dist/assets/strategy-detail-page-DsgFTd-U.js +1 -0
  92. package/dist/assets/{strategy-dialog-DMyRKrWf.js → strategy-dialog-7X7peRzu.js} +1 -1
  93. package/dist/assets/{surface-Bfz_sLX6.js → surface-CJI17F3n.js} +1 -1
  94. package/dist/assets/{table-C0VTeqw0.js → table-BNqMG3_S.js} +1 -1
  95. package/dist/assets/task-detail-page-M1sIIPA8.js +1 -0
  96. package/dist/assets/timebox-planning-dialog-ByojN0AN.js +1 -0
  97. package/dist/assets/today-page-BjDijtn8.js +1 -0
  98. package/dist/assets/training-load-page-BPRmaWmF.js +1 -0
  99. package/dist/assets/{ui-CsEkP2V8.js → ui-C1iwpj2-.js} +1 -1
  100. package/dist/assets/{use-psyche-focus-target-qxT5Oy_z.js → use-psyche-focus-target-Ct-acS9G.js} +1 -1
  101. package/dist/assets/{vendor-kIz9EZnX.js → vendor-Dnkkx2co.js} +3 -3
  102. package/dist/assets/{vitals-page-Dz1Jt5H8.js → vitals-page-CE9zdGLF.js} +1 -1
  103. package/dist/assets/weekly-review-page-CzhTv90n.js +1 -0
  104. package/dist/assets/weight-loss-page-Bp_cIk78.js +5 -0
  105. package/dist/assets/wiki-article-markdown-Dok2uy2p.js +4 -0
  106. package/dist/assets/wiki-editor-page-BuW32Y3f.js +26 -0
  107. package/dist/assets/wiki-ingest-history-page-DZKSBNHV.js +1 -0
  108. package/dist/assets/wiki-ingest-modal-DyqBEZcC.js +1 -0
  109. package/dist/assets/wiki-page-DqxlbLKG.js +1 -0
  110. package/dist/assets/workbench-flow-page-C4nc9jUg.js +5 -0
  111. package/dist/assets/workbench-page-BnyR7SL0.js +1 -0
  112. package/dist/assets/{workout-detail-page-DD9IGN6l.js → workout-detail-page-DT-c5cHL.js} +2 -2
  113. package/dist/index.html +9 -9
  114. package/dist/server/server/src/app.js +22 -3
  115. package/dist/server/server/src/health-weight-loss.js +114 -39
  116. package/dist/server/server/src/repositories/model-settings.js +9 -3
  117. package/dist/server/server/src/watch-mobile.js +28 -0
  118. package/dist/server/src/components/workbench-boxes/shared/generic-node-view.js +13 -5
  119. package/dist/server/src/lib/api.js +10 -1
  120. package/openclaw.plugin.json +1 -1
  121. package/package.json +1 -1
  122. package/dist/assets/activity-page-CgF7K2ww.js +0 -1
  123. package/dist/assets/calendar-page-C1Wfd2Fy.js +0 -1
  124. package/dist/assets/calendar-rules-CaZXtlxt.js +0 -1
  125. package/dist/assets/companion-sync-lab-page-NgeK-O-P.js +0 -1
  126. package/dist/assets/entity-note-count-link-BrS1-O0o.js +0 -1
  127. package/dist/assets/execution-board-CpO2ch6v.js +0 -1
  128. package/dist/assets/floating-action-menu-zzC2r0Ob.js +0 -1
  129. package/dist/assets/goal-detail-page-JK_Nva8e.js +0 -1
  130. package/dist/assets/goals-page-yeoJ06Vw.js +0 -1
  131. package/dist/assets/habits-page-Dx5EhkJi.js +0 -1
  132. package/dist/assets/index-BHIKoiZ6.js +0 -19
  133. package/dist/assets/index-H8R-ABM3.css +0 -1
  134. package/dist/assets/insights-page-CujrosD_.js +0 -8
  135. package/dist/assets/kanban-page-Q9NuIz5w.js +0 -1
  136. package/dist/assets/knowledge-graph-page-DaJmlvvM.js +0 -1
  137. package/dist/assets/life-force-workspace-CLVexVnb.js +0 -1
  138. package/dist/assets/movement-page-ClaoTNuX.js +0 -1
  139. package/dist/assets/notes-page-DYI8s3NN.js +0 -1
  140. package/dist/assets/pill-cluster-7UZd_lHp.js +0 -1
  141. package/dist/assets/preferences-page-BrKEkmfD.js +0 -1
  142. package/dist/assets/project-collections-CZagCmeH.js +0 -1
  143. package/dist/assets/project-management-hierarchy-page-Dr7hW9PW.js +0 -1
  144. package/dist/assets/project-management-section-nav-BLdAgTge.js +0 -1
  145. package/dist/assets/projects-page-UKrby_5-.js +0 -1
  146. package/dist/assets/rewards-page-PZFPa6rR.js +0 -1
  147. package/dist/assets/scheduling-rules-editor-B9KNKsQz.js +0 -1
  148. package/dist/assets/settings-agents-page-DPx2F6wf.js +0 -6
  149. package/dist/assets/settings-bin-page-COwP3gjo.js +0 -1
  150. package/dist/assets/settings-calendar-page-C-ghE0YT.js +0 -5
  151. package/dist/assets/settings-data-page-CBubzKBw.js +0 -1
  152. package/dist/assets/settings-logs-page-uOuXMeBm.js +0 -1
  153. package/dist/assets/settings-mobile-page-C5rfj8_r.js +0 -1
  154. package/dist/assets/settings-models-page-DijmUWdU.js +0 -1
  155. package/dist/assets/settings-page-B4u5TR5g.js +0 -1
  156. package/dist/assets/settings-rewards-page-COiTwkMH.js +0 -1
  157. package/dist/assets/settings-users-page-DEGa5DNn.js +0 -1
  158. package/dist/assets/settings-wiki-page-Cvsiz5_e.js +0 -1
  159. package/dist/assets/sleep-page-BnSOwkEU.js +0 -1
  160. package/dist/assets/sports-page-l1RqXzA_.js +0 -1
  161. package/dist/assets/strategies-page-DEnPlpAs.js +0 -1
  162. package/dist/assets/strategy-detail-page-Ls8bxKeH.js +0 -1
  163. package/dist/assets/task-detail-page-CgrYgQLD.js +0 -1
  164. package/dist/assets/timebox-planning-dialog-Ww0NGLLo.js +0 -1
  165. package/dist/assets/today-page-CIuFHMi1.js +0 -1
  166. package/dist/assets/training-load-page-BIwc648i.js +0 -1
  167. package/dist/assets/weekly-review-page-BFpBe1kI.js +0 -1
  168. package/dist/assets/weight-loss-page-BgMoBpBt.js +0 -5
  169. package/dist/assets/wiki-article-markdown-gsPTXTg1.js +0 -4
  170. package/dist/assets/wiki-editor-page-BpAZHooY.js +0 -26
  171. package/dist/assets/wiki-ingest-history-page-C-ig8O22.js +0 -1
  172. package/dist/assets/wiki-ingest-modal-BK4eQgqs.js +0 -1
  173. package/dist/assets/wiki-page-CMTZ60Zt.js +0 -1
  174. package/dist/assets/workbench-flow-page-BIpWUcLJ.js +0 -5
  175. package/dist/assets/workbench-page-CtCjYSRe.js +0 -1
@@ -3,6 +3,7 @@ import { z } from "zod";
3
3
  import { getDatabase, runInTransaction } from "./db.js";
4
4
  import { getSettings } from "./repositories/settings.js";
5
5
  import { getDefaultUser, resolveUserForMutation } from "./repositories/users.js";
6
+ import { readEncryptedSecret } from "./repositories/calendar.js";
6
7
  const optionalNumberSchema = z
7
8
  .union([z.coerce.number().finite(), z.null()])
8
9
  .optional();
@@ -88,6 +89,11 @@ export const nutritionDailyActiveCaloriesUpdateSchema = z.object({
88
89
  export const nutritionFoodLogCreateSchema = z.object({
89
90
  userId: z.string().trim().min(1).optional(),
90
91
  loggedAt: z.string().datetime().optional(),
92
+ dayKey: z
93
+ .string()
94
+ .regex(/^\d{4}-\d{2}-\d{2}$/)
95
+ .nullable()
96
+ .optional(),
91
97
  mealLabel: z.string().trim().default(""),
92
98
  source: z
93
99
  .enum(["manual", "search", "barcode", "chatgpt", "photo", "saved_meal"])
@@ -210,6 +216,9 @@ function newId(prefix) {
210
216
  function dayKey(value) {
211
217
  return value.slice(0, 10);
212
218
  }
219
+ function normalizeDayKey(value, fallbackIso) {
220
+ return value ?? dayKey(fallbackIso);
221
+ }
213
222
  function jsonString(value) {
214
223
  return JSON.stringify(value ?? null);
215
224
  }
@@ -293,10 +302,9 @@ function getDailyEnergyOverride(userId, dateKey) {
293
302
  return mapDailyEnergyOverride(row);
294
303
  }
295
304
  function buildStoredEnergyModel(input) {
296
- const today = new Date();
297
- const start = new Date(today);
298
- start.setUTCDate(today.getUTCDate() - 6);
299
- const todayKey = today.toISOString().slice(0, 10);
305
+ const todayKey = input.dateKey;
306
+ const start = new Date(`${todayKey}T00:00:00.000Z`);
307
+ start.setUTCDate(start.getUTCDate() - 6);
300
308
  const startKey = start.toISOString().slice(0, 10);
301
309
  const dailySummaryRows = getDatabase()
302
310
  .prepare(`SELECT date_key, metrics_json
@@ -344,6 +352,25 @@ function buildStoredEnergyModel(input) {
344
352
  row.date_key,
345
353
  n(row.movement_calories_kcal) || null
346
354
  ]));
355
+ const todayWorkoutEnergyFromRange = input.dayStartAt && input.dayEndAt
356
+ ? getDatabase()
357
+ .prepare(`SELECT SUM(active_energy_kcal) AS active_energy_kcal,
358
+ SUM(total_energy_kcal) AS total_energy_kcal
359
+ FROM health_workout_sessions
360
+ WHERE user_id = ?
361
+ AND started_at >= ?
362
+ AND started_at < ?`)
363
+ .get(input.userId, input.dayStartAt, input.dayEndAt)
364
+ : null;
365
+ const todayMovementCaloriesFromRange = input.dayStartAt && input.dayEndAt
366
+ ? getDatabase()
367
+ .prepare(`SELECT SUM(calories_kcal) AS movement_calories_kcal
368
+ FROM movement_trips
369
+ WHERE user_id = ?
370
+ AND started_at >= ?
371
+ AND started_at < ?`)
372
+ .get(input.userId, input.dayStartAt, input.dayEndAt)
373
+ : null;
347
374
  const activeEnergyAverage = average(dailyHealthKit.map((day) => day.activeEnergyKcal));
348
375
  const restingEnergyAverage = average(dailyHealthKit.map((day) => day.restingEnergyKcal));
349
376
  const workoutEnergyAverage = average([...workoutByDay.values()]);
@@ -356,8 +383,14 @@ function buildStoredEnergyModel(input) {
356
383
  const todayHealthKitActive = dailyHealthKit.find((day) => day.dateKey === todayKey)?.activeEnergyKcal ??
357
384
  null;
358
385
  const todayStepCount = dailyHealthKit.find((day) => day.dateKey === todayKey)?.stepCount ?? null;
359
- const todayWorkoutEnergy = workoutByDay.get(todayKey) ?? null;
360
- const todayMovementCalories = movementByDay.get(todayKey) ?? null;
386
+ const todayWorkoutEnergy = todayWorkoutEnergyFromRange != null
387
+ ? n(todayWorkoutEnergyFromRange.active_energy_kcal) ||
388
+ n(todayWorkoutEnergyFromRange.total_energy_kcal) ||
389
+ null
390
+ : (workoutByDay.get(todayKey) ?? null);
391
+ const todayMovementCalories = todayMovementCaloriesFromRange != null
392
+ ? n(todayMovementCaloriesFromRange.movement_calories_kcal) || null
393
+ : (movementByDay.get(todayKey) ?? null);
361
394
  const todayWorkoutMovementCalories = todayWorkoutEnergy != null || todayMovementCalories != null
362
395
  ? n(todayWorkoutEnergy) + n(todayMovementCalories)
363
396
  : null;
@@ -365,27 +398,35 @@ function buildStoredEnergyModel(input) {
365
398
  stepCount: todayStepCount,
366
399
  weightKg: input.latestWeightKg
367
400
  });
401
+ const todayStepCaloriesForActiveBudget = todayStepEstimatedCalories != null &&
402
+ (todayWorkoutEnergy != null ||
403
+ todayMovementCalories != null ||
404
+ todayStepEstimatedCalories > baselineActiveCalories)
405
+ ? todayStepEstimatedCalories
406
+ : null;
368
407
  const todayFallbackPartCount = [
369
408
  todayWorkoutEnergy,
370
409
  todayMovementCalories,
371
- todayStepEstimatedCalories
410
+ todayStepCaloriesForActiveBudget
372
411
  ].filter((value) => value != null).length;
373
412
  const todayFallbackActiveCalories = todayFallbackPartCount > 0
374
413
  ? n(todayWorkoutEnergy) +
375
414
  n(todayMovementCalories) +
376
- n(todayStepEstimatedCalories)
415
+ n(todayStepCaloriesForActiveBudget)
377
416
  : null;
378
417
  const todayObservedActiveCalories = todayHealthKitActive ?? todayFallbackActiveCalories;
379
418
  const todayFallbackSource = (() => {
380
419
  if (todayWorkoutEnergy != null &&
381
420
  todayMovementCalories != null &&
382
- todayStepEstimatedCalories != null) {
421
+ todayStepCaloriesForActiveBudget != null) {
383
422
  return "today_workout_movement_step_energy";
384
423
  }
385
- if (todayWorkoutEnergy != null && todayStepEstimatedCalories != null) {
424
+ if (todayWorkoutEnergy != null &&
425
+ todayStepCaloriesForActiveBudget != null) {
386
426
  return "today_workout_step_energy";
387
427
  }
388
- if (todayMovementCalories != null && todayStepEstimatedCalories != null) {
428
+ if (todayMovementCalories != null &&
429
+ todayStepCaloriesForActiveBudget != null) {
389
430
  return "today_movement_step_energy";
390
431
  }
391
432
  if (todayWorkoutEnergy != null && todayMovementCalories != null) {
@@ -397,7 +438,7 @@ function buildStoredEnergyModel(input) {
397
438
  if (todayMovementCalories != null) {
398
439
  return "today_movement_trip_calories";
399
440
  }
400
- if (todayStepEstimatedCalories != null) {
441
+ if (todayStepCaloriesForActiveBudget != null) {
401
442
  return "today_step_estimate";
402
443
  }
403
444
  return "default_active_calories";
@@ -416,7 +457,8 @@ function buildStoredEnergyModel(input) {
416
457
  const estimatedTdeeKcal = activeBurnKcal != null && restingEnergyAverage != null
417
458
  ? round(activeBurnKcal + restingEnergyAverage, 0)
418
459
  : input.inferredTdee;
419
- const hasHealthKitEnergy = activeEnergyAverage != null ||
460
+ const hasHealthKitDailyActiveEnergy = activeEnergyAverage != null;
461
+ const hasHealthKitEnergy = hasHealthKitDailyActiveEnergy ||
420
462
  restingEnergyAverage != null ||
421
463
  workoutEnergyAverage != null;
422
464
  const hasMovementEnergy = movementCaloriesAverage != null;
@@ -454,6 +496,8 @@ function buildStoredEnergyModel(input) {
454
496
  : null,
455
497
  workoutEnergyKcal: workoutEnergyAverage != null ? round(workoutEnergyAverage, 0) : null,
456
498
  averageCalorieIntake: input.averageCalories,
499
+ recentFoodLogCount: input.recentFoodLogCount,
500
+ recentFoodLogDayCount: input.recentFoodLogDayCount,
457
501
  currentDeficitEstimate: estimatedTdeeKcal != null
458
502
  ? round(input.averageCalories - estimatedTdeeKcal, 0)
459
503
  : null,
@@ -469,7 +513,7 @@ function buildStoredEnergyModel(input) {
469
513
  exerciseMinutesAverage: average(dailyHealthKit.map((day) => day.exerciseMinutes)),
470
514
  stepCountAverage: average(dailyHealthKit.map((day) => day.stepCount)),
471
515
  sourceAvailability: {
472
- healthKitDailyEnergy: hasHealthKitEnergy,
516
+ healthKitDailyEnergy: hasHealthKitDailyActiveEnergy,
473
517
  movementTripCalories: hasMovementEnergy,
474
518
  workoutEnergy: workoutEnergyAverage != null
475
519
  }
@@ -657,6 +701,7 @@ export function createNutritionFoodLog(input) {
657
701
  const parsed = nutritionFoodLogCreateSchema.parse(input);
658
702
  const userId = resolveWriteUser(parsed.userId);
659
703
  const loggedAt = parsed.loggedAt ?? nowIso();
704
+ const logDayKey = normalizeDayKey(parsed.dayKey, loggedAt);
660
705
  const id = newId("meal");
661
706
  const now = nowIso();
662
707
  runInTransaction(() => {
@@ -666,7 +711,7 @@ export function createNutritionFoodLog(input) {
666
711
  place_id, stay_id, workout_id, sleep_id, day_key, image_refs_json,
667
712
  parser_provenance_json, links_json, created_at, updated_at
668
713
  ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
669
- .run(id, userId, loggedAt, parsed.mealLabel, parsed.source, parsed.confirmationState, parsed.notes, parsed.placeId ?? null, parsed.stayId ?? null, parsed.workoutId ?? null, parsed.sleepId ?? null, dayKey(loggedAt), jsonString(parsed.imageRefs), jsonString(parsed.parserProvenance), jsonString(parsed.links), now, now);
714
+ .run(id, userId, loggedAt, parsed.mealLabel, parsed.source, parsed.confirmationState, parsed.notes, parsed.placeId ?? null, parsed.stayId ?? null, parsed.workoutId ?? null, parsed.sleepId ?? null, logDayKey, jsonString(parsed.imageRefs), jsonString(parsed.parserProvenance), jsonString(parsed.links), now, now);
670
715
  for (const item of parsed.items) {
671
716
  insertMealItem(id, item);
672
717
  }
@@ -680,6 +725,7 @@ export function patchNutritionFoodLog(logId, input) {
680
725
  return null;
681
726
  }
682
727
  const nextLoggedAt = parsed.loggedAt ?? existing.loggedAt;
728
+ const nextDayKey = normalizeDayKey(parsed.dayKey, nextLoggedAt);
683
729
  const now = nowIso();
684
730
  runInTransaction(() => {
685
731
  getDatabase()
@@ -689,7 +735,7 @@ export function patchNutritionFoodLog(logId, input) {
689
735
  day_key = ?, image_refs_json = ?, parser_provenance_json = ?,
690
736
  links_json = ?, updated_at = ?
691
737
  WHERE id = ?`)
692
- .run(nextLoggedAt, parsed.mealLabel ?? existing.mealLabel, parsed.source ?? existing.source, parsed.confirmationState ?? existing.confirmationState, parsed.notes ?? existing.notes, parsed.placeId !== undefined ? parsed.placeId : existing.placeId, parsed.stayId !== undefined ? parsed.stayId : existing.stayId, parsed.workoutId !== undefined ? parsed.workoutId : existing.workoutId, parsed.sleepId !== undefined ? parsed.sleepId : existing.sleepId, dayKey(nextLoggedAt), jsonString(parsed.imageRefs ?? existing.imageRefs), jsonString(parsed.parserProvenance ?? existing.parserProvenance), jsonString(parsed.links ?? existing.links), now, logId);
738
+ .run(nextLoggedAt, parsed.mealLabel ?? existing.mealLabel, parsed.source ?? existing.source, parsed.confirmationState ?? existing.confirmationState, parsed.notes ?? existing.notes, parsed.placeId !== undefined ? parsed.placeId : existing.placeId, parsed.stayId !== undefined ? parsed.stayId : existing.stayId, parsed.workoutId !== undefined ? parsed.workoutId : existing.workoutId, parsed.sleepId !== undefined ? parsed.sleepId : existing.sleepId, nextDayKey, jsonString(parsed.imageRefs ?? existing.imageRefs), jsonString(parsed.parserProvenance ?? existing.parserProvenance), jsonString(parsed.links ?? existing.links), now, logId);
693
739
  if (parsed.items) {
694
740
  getDatabase()
695
741
  .prepare(`DELETE FROM nutrition_meal_items WHERE log_id = ?`)
@@ -1035,9 +1081,8 @@ function listExperiments(userId, limit = 20) {
1035
1081
  updatedAt: row.updated_at
1036
1082
  }));
1037
1083
  }
1038
- function buildTodayLedger(logs, target, dynamicTargetCalories, activeAdjustmentCalories, activeCaloriesSource) {
1039
- const today = new Date().toISOString().slice(0, 10);
1040
- const todayLogs = logs.filter((log) => log.dayKey === today);
1084
+ function buildTodayLedger(logs, target, dateKey, dynamicTargetCalories, activeAdjustmentCalories, activeCaloriesSource) {
1085
+ const todayLogs = logs.filter((log) => log.dayKey === dateKey);
1041
1086
  const totals = todayLogs.reduce((acc, log) => ({
1042
1087
  calories: acc.calories + log.totals.calories,
1043
1088
  proteinGrams: acc.proteinGrams + log.totals.proteinGrams,
@@ -1058,7 +1103,7 @@ function buildTodayLedger(logs, target, dynamicTargetCalories, activeAdjustmentC
1058
1103
  alcoholGrams: 0
1059
1104
  });
1060
1105
  return {
1061
- dateKey: today,
1106
+ dateKey,
1062
1107
  meals: todayLogs,
1063
1108
  totals,
1064
1109
  plannedTargetCalories: target.calorieTarget,
@@ -1267,10 +1312,10 @@ function buildGeneratedHypotheses(logs, subjective, gut, appearance) {
1267
1312
  }
1268
1313
  return cards;
1269
1314
  }
1270
- export function getWeightLossViewData(userIds) {
1315
+ export function getWeightLossViewData(userIds, options = {}) {
1271
1316
  const generatedAt = new Date().toISOString();
1272
1317
  const userId = resolveReadUser(userIds);
1273
- const todayKey = generatedAt.slice(0, 10);
1318
+ const todayKey = options.dateKey ?? generatedAt.slice(0, 10);
1274
1319
  const targetRow = getDatabase()
1275
1320
  .prepare(`SELECT * FROM nutrition_targets WHERE user_id = ?`)
1276
1321
  .get(userId);
@@ -1287,7 +1332,10 @@ export function getWeightLossViewData(userIds) {
1287
1332
  const recentLogs = logs.slice(0, 14);
1288
1333
  const recentTotals = sumItems(recentLogs.flatMap((log) => log.items));
1289
1334
  const trackedDays = new Set(logs.map((log) => log.dayKey)).size;
1290
- const averageCalories = trackedDays > 0 ? round(recentTotals.calories / trackedDays, 0) : 0;
1335
+ const recentTrackedDays = new Set(recentLogs.map((log) => log.dayKey)).size;
1336
+ const averageCalories = recentTrackedDays > 0
1337
+ ? round(recentTotals.calories / recentTrackedDays, 0)
1338
+ : 0;
1291
1339
  const inferredTdee = target.calorieTarget != null
1292
1340
  ? round(target.calorieTarget + Math.abs(n(target.weeklyRateGoalKg)) * 1100, 0)
1293
1341
  : null;
@@ -1295,14 +1343,19 @@ export function getWeightLossViewData(userIds) {
1295
1343
  const dailyActiveOverride = getDailyEnergyOverride(userId, todayKey);
1296
1344
  const energyModel = buildStoredEnergyModel({
1297
1345
  userId,
1346
+ dateKey: todayKey,
1347
+ dayStartAt: options.dayStartAt,
1348
+ dayEndAt: options.dayEndAt,
1298
1349
  inferredTdee,
1299
1350
  averageCalories,
1351
+ recentFoodLogCount: recentLogs.length,
1352
+ recentFoodLogDayCount: recentTrackedDays,
1300
1353
  defaultActiveCalories,
1301
1354
  latestWeightKg: weightTrend.latestWeightKg,
1302
1355
  dailyActiveOverride
1303
1356
  });
1304
1357
  const todayTargetCalories = Math.max(0, round(target.calorieTarget + energyModel.todayTargetAdjustmentKcal, 0));
1305
- const todayLedger = buildTodayLedger(logs, target, todayTargetCalories, energyModel.todayTargetAdjustmentKcal, energyModel.todayActiveCaloriesSource);
1358
+ const todayLedger = buildTodayLedger(logs, target, todayKey, todayTargetCalories, energyModel.todayTargetAdjustmentKcal, energyModel.todayActiveCaloriesSource);
1306
1359
  return {
1307
1360
  generatedAt,
1308
1361
  userId,
@@ -1641,25 +1694,13 @@ function extractJsonObject(text) {
1641
1694
  }
1642
1695
  return trimmed;
1643
1696
  }
1644
- function getNutritionCodexProfile(connectionId) {
1645
- const settings = getSettings();
1646
- const selectedConnectionId = connectionId?.trim() ||
1647
- settings.modelSettings.forgeAgent.basicChat.connectionId ||
1648
- settings.modelSettings.forgeAgent.wiki.connectionId ||
1649
- "";
1650
- if (!selectedConnectionId) {
1651
- return null;
1652
- }
1653
- const row = getDatabase()
1654
- .prepare(`SELECT provider, auth_mode, base_url, model, secret_id, enabled
1655
- FROM ai_model_connections
1656
- WHERE id = ?`)
1657
- .get(selectedConnectionId);
1697
+ function mapNutritionCodexProfileRow(row) {
1658
1698
  if (!row ||
1659
1699
  row.enabled !== 1 ||
1660
1700
  row.provider !== "openai-codex" ||
1661
1701
  row.auth_mode !== "oauth" ||
1662
- !row.secret_id) {
1702
+ !row.secret_id ||
1703
+ !readEncryptedSecret(row.secret_id)) {
1663
1704
  return null;
1664
1705
  }
1665
1706
  return {
@@ -1671,6 +1712,40 @@ function getNutritionCodexProfile(connectionId) {
1671
1712
  metadata: {}
1672
1713
  };
1673
1714
  }
1715
+ function getNutritionCodexProfile(connectionId) {
1716
+ const settings = getSettings();
1717
+ const selectedConnectionIds = [
1718
+ connectionId?.trim(),
1719
+ settings.modelSettings.forgeAgent.basicChat.connectionId,
1720
+ settings.modelSettings.forgeAgent.wiki.connectionId
1721
+ ].filter((id) => Boolean(id));
1722
+ for (const selectedConnectionId of new Set(selectedConnectionIds)) {
1723
+ const row = getDatabase()
1724
+ .prepare(`SELECT provider, auth_mode, base_url, model, secret_id, enabled
1725
+ FROM ai_model_connections
1726
+ WHERE id = ?`)
1727
+ .get(selectedConnectionId);
1728
+ const profile = mapNutritionCodexProfileRow(row);
1729
+ if (profile) {
1730
+ return profile;
1731
+ }
1732
+ }
1733
+ const fallbackRows = getDatabase()
1734
+ .prepare(`SELECT provider, auth_mode, base_url, model, secret_id, enabled
1735
+ FROM ai_model_connections
1736
+ WHERE provider = 'openai-codex'
1737
+ AND auth_mode = 'oauth'
1738
+ AND enabled = 1
1739
+ ORDER BY updated_at DESC`)
1740
+ .all();
1741
+ for (const row of fallbackRows) {
1742
+ const profile = mapNutritionCodexProfileRow(row);
1743
+ if (profile) {
1744
+ return profile;
1745
+ }
1746
+ }
1747
+ return null;
1748
+ }
1674
1749
  const parsedMealItemSchema = z.object({
1675
1750
  name: z.string().trim().min(1),
1676
1751
  quantity: z.coerce.number().positive().default(1),
@@ -124,15 +124,16 @@ export function upsertAiModelConnection(input, secrets, options = {}) {
124
124
  WHERE id = ?`)
125
125
  .get(parsed.id.trim())
126
126
  : undefined;
127
- const id = existing?.id ??
128
- `mdl_${randomUUID().replaceAll("-", "").slice(0, 10)}`;
127
+ const id = existing?.id ?? `mdl_${randomUUID().replaceAll("-", "").slice(0, 10)}`;
129
128
  const now = new Date().toISOString();
130
129
  const provider = parsed.provider;
131
130
  const authMode = parsed.authMode ?? defaultAuthMode(provider);
132
131
  const baseUrl = parsed.baseUrl?.trim() ||
133
132
  existing?.base_url ||
134
133
  defaultBaseUrlForProvider(provider);
135
- let secretId = existing?.secret_id ?? null;
134
+ let secretId = existing?.secret_id && readEncryptedSecret(existing.secret_id)
135
+ ? existing.secret_id
136
+ : null;
136
137
  let accountLabel = existing?.account_label ?? null;
137
138
  if (parsed.provider === "mock") {
138
139
  secretId = null;
@@ -152,6 +153,11 @@ export function upsertAiModelConnection(input, secrets, options = {}) {
152
153
  accountLabel = options.oauthCredential.accountId;
153
154
  storeEncryptedSecret(secretId, secrets.sealJson(options.oauthCredential), `${parsed.label} AI OAuth connection`);
154
155
  }
156
+ if (parsed.provider !== "mock" && !secretId) {
157
+ throw new Error(authMode === "oauth"
158
+ ? "Reconnect OAuth before saving this model connection. The existing credential is missing from Forge's encrypted secret store."
159
+ : "Enter the API key before saving this model connection. The existing credential is missing from Forge's encrypted secret store.");
160
+ }
155
161
  getDatabase()
156
162
  .prepare(`INSERT INTO ai_model_connections (
157
163
  id, label, provider, auth_mode, base_url, model, account_label, secret_id, enabled, metadata_json, created_at, updated_at
@@ -968,8 +968,24 @@ function processWatchCommand(pairing, command) {
968
968
  }
969
969
  case "capture_event": {
970
970
  const rawPayload = command.payload;
971
+ const reservedCapturePayloadKeys = new Set([
972
+ "eventType",
973
+ "recordedAt",
974
+ "promptId",
975
+ "linkedContext",
976
+ "placeId",
977
+ "stayId",
978
+ "tripId",
979
+ "workoutId"
980
+ ]);
981
+ const eventPayload = typeof rawPayload.payload === "object" &&
982
+ rawPayload.payload != null &&
983
+ Array.isArray(rawPayload.payload) === false
984
+ ? rawPayload.payload
985
+ : Object.fromEntries(Object.entries(rawPayload).filter(([key]) => !reservedCapturePayloadKeys.has(key)));
971
986
  const payload = captureCommandPayloadSchema.parse({
972
987
  ...rawPayload,
988
+ payload: eventPayload,
973
989
  linkedContext: typeof rawPayload.linkedContext === "object" &&
974
990
  rawPayload.linkedContext != null &&
975
991
  Array.isArray(rawPayload.linkedContext) === false
@@ -1101,6 +1117,15 @@ function commandErrorPayload(error) {
1101
1117
  message: error instanceof Error ? error.message : "Unknown watch command error"
1102
1118
  };
1103
1119
  }
1120
+ function isPermanentWatchCommandError(error) {
1121
+ if (error instanceof ZodError) {
1122
+ return true;
1123
+ }
1124
+ if (error instanceof HttpError) {
1125
+ return error.statusCode >= 400 && error.statusCode < 500;
1126
+ }
1127
+ return false;
1128
+ }
1104
1129
  export function ingestWatchCommandBatch(pairing, input) {
1105
1130
  assertWatchReady(pairing);
1106
1131
  const parsed = mobileWatchCommandBatchSchema.parse(input);
@@ -1126,6 +1151,9 @@ export function ingestWatchCommandBatch(pairing, input) {
1126
1151
  }));
1127
1152
  }
1128
1153
  catch (error) {
1154
+ if (!isPermanentWatchCommandError(error)) {
1155
+ throw error;
1156
+ }
1129
1157
  const errorPayload = commandErrorPayload(error);
1130
1158
  receipts.push(writeActionReceipt(pairing, command, {
1131
1159
  status: "failed",
@@ -3,22 +3,30 @@ import { Handle, Position } from "@xyflow/react";
3
3
  import { useState } from "react";
4
4
  import { InfoTooltip } from "../../../components/ui/info-tooltip.js";
5
5
  import { cn } from "../../../lib/utils.js";
6
+ const nodeEyebrowClass = "flex items-center gap-1 text-[10px] uppercase tracking-[0.18em] text-[var(--ui-ink-faint)]";
7
+ const nodePillClass = "rounded-full border border-[var(--ui-border-subtle)] bg-[var(--ui-surface-2)] px-2.5 py-1 text-[10px] uppercase tracking-[0.16em] text-[var(--ui-ink-soft)]";
8
+ const nodeSoftPanelClass = "rounded-[18px] border border-[var(--ui-border-subtle)] bg-[var(--ui-surface-2)] px-3 py-2 text-[11px] text-[var(--ui-ink-soft)]";
9
+ const nodeCodePanelClass = "rounded-[18px] border border-[var(--ui-border-subtle)] bg-[var(--ui-code-bg)] p-3 text-[var(--ui-code-text)]";
6
10
  function describePort(port) {
7
- return [port.kind, port.modelName, port.itemKind ? `item:${port.itemKind}` : null]
11
+ return [
12
+ port.kind,
13
+ port.modelName,
14
+ port.itemKind ? `item:${port.itemKind}` : null
15
+ ]
8
16
  .filter(Boolean)
9
17
  .join(" · ");
10
18
  }
11
19
  function PortList({ title, ports, align }) {
12
- return (_jsxs("div", { className: "grid gap-1.5", children: [_jsxs("div", { className: cn("flex items-center gap-1 text-[10px] uppercase tracking-[0.18em] text-white/34", align === "left" ? "text-left" : "text-right"), children: [_jsx("span", { children: title }), _jsx(InfoTooltip, { content: align === "left"
20
+ return (_jsxs("div", { className: "grid gap-1.5", children: [_jsxs("div", { className: cn(nodeEyebrowClass, align === "left" ? "text-left" : "text-right"), children: [_jsx("span", { children: title }), _jsx(InfoTooltip, { content: align === "left"
13
21
  ? "Inputs are values this box expects from upstream nodes."
14
- : "Outputs are values this box publishes for downstream nodes.", label: align === "left" ? "Explain box inputs" : "Explain box outputs" })] }), ports.length === 0 ? (_jsx("div", { className: "rounded-full border border-dashed border-white/10 px-3 py-1.5 text-[11px] text-white/28", children: "None" })) : null, ports.map((port) => (_jsxs("div", { className: cn("relative rounded-[16px] bg-white/[0.05] px-3 py-2 text-[11px] text-white/62", align === "left" ? "pl-5 text-left" : "pr-5 text-right"), children: [_jsx(Handle, { type: align === "left" ? "target" : "source", position: align === "left" ? Position.Left : Position.Right, id: port.key, className: "!size-2.5 !border !border-white/80 !bg-[#b8c5ff]", style: {
22
+ : "Outputs are values this box publishes for downstream nodes.", label: align === "left" ? "Explain box inputs" : "Explain box outputs" })] }), ports.length === 0 ? (_jsx("div", { className: "rounded-full border border-dashed border-[var(--ui-border-subtle)] px-3 py-1.5 text-[11px] text-[var(--ui-ink-faint)]", children: "None" })) : null, ports.map((port) => (_jsxs("div", { className: cn("relative rounded-[16px] border border-[var(--ui-border-subtle)] bg-[var(--ui-surface-2)] px-3 py-2 text-[11px] text-[var(--ui-ink-soft)]", align === "left" ? "pl-5 text-left" : "pr-5 text-right"), children: [_jsx(Handle, { type: align === "left" ? "target" : "source", position: align === "left" ? Position.Left : Position.Right, id: port.key, className: "!size-2.5 !border !border-[var(--ui-surface-1)] !bg-[var(--primary)]", style: {
15
23
  [align]: 6
16
- } }), _jsx("div", { children: port.label }), _jsx("div", { className: "mt-1 text-[10px] text-white/38", children: describePort(port) })] }, port.key)))] }));
24
+ } }), _jsx("div", { children: port.label }), _jsx("div", { className: "mt-1 text-[10px] text-[var(--ui-ink-faint)]", children: describePort(port) })] }, port.key)))] }));
17
25
  }
18
26
  export function createGenericWorkbenchNodeView(definition) {
19
27
  return function GenericWorkbenchNodeView(_props) {
20
28
  const [schemaOpen, setSchemaOpen] = useState(false);
21
- return (_jsxs("div", { className: "min-w-[280px] rounded-[24px] border border-white/10 bg-[linear-gradient(180deg,rgba(20,28,45,0.98),rgba(11,16,29,0.98))] p-3 shadow-[0_26px_80px_rgba(0,0,0,0.4)]", children: [_jsxs("div", { className: "flex items-start justify-between gap-3", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("div", { className: "truncate text-sm font-semibold text-white", children: definition.title }), _jsx("div", { className: "mt-1 line-clamp-2 text-[12px] leading-5 text-white/48", children: definition.description })] }), _jsx("div", { className: "rounded-full bg-white/[0.06] px-2.5 py-1 text-[10px] uppercase tracking-[0.16em] text-white/56", children: "box" })] }), definition.params.length > 0 ? (_jsxs("div", { className: "mt-3 rounded-[18px] bg-white/[0.04] px-3 py-2 text-[11px] text-white/52", children: [definition.params.length, " param", definition.params.length === 1 ? "" : "s", " configurable in the flow editor"] })) : null, definition.tools.length > 0 ? (_jsxs("div", { className: "mt-2 rounded-[18px] bg-white/[0.04] px-3 py-2 text-[11px] text-white/52", children: [definition.tools.length, " tool", definition.tools.length === 1 ? "" : "s", " available"] })) : null, _jsx("div", { className: "mt-2", children: _jsx("button", { type: "button", className: "rounded-full bg-white/[0.05] px-3 py-1.5 text-[11px] text-white/56 transition hover:bg-white/[0.08] hover:text-white", onClick: () => setSchemaOpen((current) => !current), children: schemaOpen ? "Hide schema" : "Preview schema" }) }), _jsxs("div", { className: "mt-3 grid grid-cols-[minmax(0,1fr)_minmax(0,1fr)] gap-3", children: [_jsx(PortList, { title: "Inputs", ports: definition.inputs, align: "left" }), _jsx(PortList, { title: "Outputs", ports: definition.output, align: "right" })] }), schemaOpen ? (_jsxs("div", { className: "mt-3 rounded-[18px] border border-white/8 bg-black/20 p-3", children: [_jsxs("div", { className: "flex items-center gap-2 text-[10px] uppercase tracking-[0.18em] text-white/38", children: [_jsx("span", { children: "Box contract" }), _jsx(InfoTooltip, { content: "This preview summarizes what the box consumes, publishes, and what tools it can expose to AI nodes.", label: "Explain box contract preview" })] }), _jsx("pre", { className: "mt-2 overflow-auto whitespace-pre-wrap text-[11px] leading-5 text-white/64", children: JSON.stringify({
29
+ return (_jsxs("div", { className: "min-w-[280px] rounded-[24px] border border-[var(--ui-border-subtle)] bg-[image:var(--ui-surface-modal)] p-3 shadow-[var(--ui-shadow-floating)]", children: [_jsxs("div", { className: "flex items-start justify-between gap-3", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("div", { className: "truncate text-sm font-semibold text-[var(--ui-ink-strong)]", children: definition.title }), _jsx("div", { className: "mt-1 line-clamp-2 text-[12px] leading-5 text-[var(--ui-ink-soft)]", children: definition.description })] }), _jsx("div", { className: nodePillClass, children: "box" })] }), definition.params.length > 0 ? (_jsxs("div", { className: `mt-3 ${nodeSoftPanelClass}`, children: [definition.params.length, " param", definition.params.length === 1 ? "" : "s", " configurable in the flow editor"] })) : null, definition.tools.length > 0 ? (_jsxs("div", { className: `mt-2 ${nodeSoftPanelClass}`, children: [definition.tools.length, " tool", definition.tools.length === 1 ? "" : "s", " available"] })) : null, _jsx("div", { className: "mt-2", children: _jsx("button", { type: "button", className: "rounded-full border border-[var(--ui-border-subtle)] bg-[var(--ui-surface-2)] px-3 py-1.5 text-[11px] text-[var(--ui-ink-soft)] transition hover:bg-[var(--ui-surface-hover)] hover:text-[var(--ui-ink-strong)]", onClick: () => setSchemaOpen((current) => !current), children: schemaOpen ? "Hide schema" : "Preview schema" }) }), _jsxs("div", { className: "mt-3 grid grid-cols-[minmax(0,1fr)_minmax(0,1fr)] gap-3", children: [_jsx(PortList, { title: "Inputs", ports: definition.inputs, align: "left" }), _jsx(PortList, { title: "Outputs", ports: definition.output, align: "right" })] }), schemaOpen ? (_jsxs("div", { className: `mt-3 ${nodeCodePanelClass}`, children: [_jsxs("div", { className: "flex items-center gap-2 text-[10px] uppercase tracking-[0.18em] text-[var(--ui-ink-faint)]", children: [_jsx("span", { children: "Box contract" }), _jsx(InfoTooltip, { content: "This preview summarizes what the box consumes, publishes, and what tools it can expose to AI nodes.", label: "Explain box contract preview" })] }), _jsx("pre", { className: "mt-2 overflow-auto whitespace-pre-wrap text-[11px] leading-5", children: JSON.stringify({
22
30
  inputs: definition.inputs.map(({ key, kind, required, description, modelName, itemKind, shape, exampleValue }) => ({
23
31
  key,
24
32
  kind,
@@ -1756,9 +1756,18 @@ export function getVitalsView(userIds) {
1756
1756
  const suffix = search.size > 0 ? `?${search.toString()}` : "";
1757
1757
  return request(`/api/v1/health/vitals${suffix}`);
1758
1758
  }
1759
- export function getWeightLossView(userIds) {
1759
+ export function getWeightLossView(userIds, options) {
1760
1760
  const search = new URLSearchParams();
1761
1761
  appendUserIds(search, coerceUserIds(userIds));
1762
+ if (options?.dateKey) {
1763
+ search.set("dateKey", options.dateKey);
1764
+ }
1765
+ if (options?.dayStartAt) {
1766
+ search.set("dayStartAt", options.dayStartAt);
1767
+ }
1768
+ if (options?.dayEndAt) {
1769
+ search.set("dayEndAt", options.dayEndAt);
1770
+ }
1762
1771
  const suffix = search.size > 0 ? `?${search.toString()}` : "";
1763
1772
  return request(`/api/v1/health/weight-loss${suffix}`);
1764
1773
  }
@@ -2,7 +2,7 @@
2
2
  "id": "forge-openclaw-plugin",
3
3
  "name": "Forge",
4
4
  "description": "Curated OpenClaw adapter for the Forge collaboration API, UI entrypoint, and localhost auto-start runtime.",
5
- "version": "0.2.102",
5
+ "version": "0.2.104",
6
6
  "activation": {
7
7
  "onStartup": true,
8
8
  "onCapabilities": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "forge-openclaw-plugin",
3
- "version": "0.2.102",
3
+ "version": "0.2.104",
4
4
  "description": "Curated OpenClaw adapter for the Forge collaboration API, UI entrypoint, and localhost auto-start runtime.",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -1 +0,0 @@
1
- import{j as e,aC as x,c2 as k}from"./vendor-kIz9EZnX.js";import{j as A,i as C,k as E}from"./state-VYvD1QVP.js";import{C as w,B as I,U as P,f,u as R,i as S,r as L,L as T,E as U,a as q,l as Q}from"./index-BHIKoiZ6.js";import{P as _}from"./page-hero-oFHaAjtL.js";import{u as F,f as g,g as H,c as M}from"./table-C0VTeqw0.js";import{g as p,a as v}from"./entity-links-DwpxhW2H.js";import"./motion-CfdU2F35.js";import"./ui-CsEkP2V8.js";import"./forms-CmLAyGqz.js";import"./board-DqHzdCPQ.js";import"./graph-BTa79qum.js";const n=M(),y=[n.accessor("title",{header:"Event",cell:t=>{const s=p(t.row.original);return s?e.jsx(x,{to:s,className:"font-medium text-white transition hover:text-[var(--primary)]",children:t.getValue()}):e.jsx("div",{className:"font-medium text-white",children:t.getValue()})}}),n.accessor("source",{header:"Source",cell:t=>e.jsx("div",{className:"font-label text-[11px] uppercase tracking-[0.16em] text-white/50",children:t.getValue()})}),n.display({id:"owner",header:"Owner",cell:t=>t.row.original.user?e.jsx(P,{user:t.row.original.user,compact:!0}):e.jsx("div",{className:"text-[11px] uppercase tracking-[0.16em] text-white/32",children:"Unowned"})}),n.accessor("createdAt",{header:"When",cell:t=>e.jsx("div",{className:"text-sm text-white/55",children:f(t.getValue())})}),n.display({id:"actions",header:"Open",cell:t=>{const s=p(t.row.original);return s?e.jsx(x,{to:s,className:"inline-flex text-[11px] uppercase tracking-[0.16em] text-[var(--primary)] transition hover:text-white",children:"Open"}):e.jsx("div",{className:"text-[11px] uppercase tracking-[0.16em] text-white/32",children:"Archive only"})}})];function V({rows:t,onRemove:s}){const l=s?[...y,n.display({id:"remove",header:"Correct",cell:i=>e.jsx(I,{variant:"ghost",className:"h-auto px-0 py-0 text-[11px] uppercase tracking-[0.16em] text-white/55",onClick:()=>{s(i.row.original.id)},children:"Remove log"})})]:y,o=F({columns:l,data:t,getCoreRowModel:H()});return e.jsx(w,{className:"overflow-hidden p-0",children:e.jsxs("table",{className:"w-full border-collapse",children:[e.jsx("thead",{children:o.getHeaderGroups().map(i=>e.jsx("tr",{className:"bg-white/[0.03]",children:i.headers.map(a=>e.jsx("th",{className:"px-5 py-4 text-left font-label text-[11px] uppercase tracking-[0.18em] text-white/40",children:a.isPlaceholder?null:g(a.column.columnDef.header,a.getContext())},a.id))},i.id))}),e.jsx("tbody",{children:o.getRowModel().rows.map(i=>e.jsx("tr",{className:"bg-white/[0.015] transition hover:bg-white/[0.035]",children:i.getVisibleCells().map(a=>e.jsx("td",{className:"px-5 py-4 align-top",children:g(a.column.columnDef.cell,a.getContext())},a.id))},i.id))})]})})}function Z(){var u;const t=R(),s=Array.isArray(t.selectedUserIds)?t.selectedUserIds:[],[l]=k(),o=A(),i=l.get("entityId")??void 0,a=l.get("entityType")??void 0,j=l.get("eventId"),d=C({queryKey:["activity-archive",a,i,...s],queryFn:()=>Q({limit:100,entityType:a,entityId:i,userIds:s})}),b=E({mutationFn:c=>L(c),onSuccess:async()=>{await Promise.all([o.invalidateQueries({queryKey:["activity-archive"]}),S(o),o.invalidateQueries({queryKey:["task-context"]}),o.invalidateQueries({queryKey:["project-board"]})])}}),m=((u=d.data)==null?void 0:u.activity)??[];if(d.isLoading)return e.jsx(T,{eyebrow:"Evidence archive",title:"Loading activity",description:"Pulling the visible audit trail, grouped evidence, and correction history."});if(d.isError)return e.jsx(U,{eyebrow:"Evidence archive",error:d.error,onRetry:()=>void d.refetch()});if(m.length===0)return e.jsx(q,{eyebrow:"Evidence archive",title:i?"No evidence matched this filter":"No activity recorded yet",description:i?"Forge could not find visible events for this specific entity yet. Try a broader archive view or create some work first.":"As you complete work, log runs, or make corrections, the audit trail will appear here."});const N=m.reduce((c,h)=>{const r=h.createdAt.slice(0,10);return c[r]=[...c[r]??[],h],c},{});return e.jsxs("div",{className:"grid gap-5",children:[e.jsx(_,{title:"Activity",description:i?"This filtered archive shows the evidence connected to the item you opened, so you can confirm what changed and when.":"Activity is your visible audit trail. Use it to inspect progress, confirm corrections, and trace work back to the goal, project, or task it came from.",badge:`${m.length} recent events`}),e.jsxs("section",{className:"grid gap-5 xl:grid-cols-[minmax(0,1.1fr)_minmax(0,0.9fr)]",children:[e.jsx(V,{rows:m,onRemove:async c=>{await b.mutateAsync(c)}}),e.jsx("div",{className:"grid gap-4",children:Object.entries(N).slice(0,6).map(([c,h])=>e.jsxs(w,{children:[e.jsx("div",{className:"font-label text-[11px] uppercase tracking-[0.18em] text-white/45",children:c}),e.jsx("div",{className:"mt-4 grid gap-3",children:h.slice(0,3).map(r=>e.jsxs("div",{className:`rounded-[18px] p-4 ${j===r.id?"bg-[rgba(192,193,255,0.12)] shadow-[inset_0_0_0_1px_rgba(192,193,255,0.2)]":"bg-white/[0.04]"}`,children:[e.jsx("div",{className:"font-medium text-white",children:r.title}),e.jsx("div",{className:"mt-2 text-sm text-white/58",children:r.description}),e.jsx("div",{className:"mt-3 text-[11px] uppercase tracking-[0.16em] text-white/35",children:f(r.createdAt)}),p(r)&&v(r)?e.jsx(x,{to:p(r),className:"mt-3 inline-flex text-[11px] uppercase tracking-[0.16em] text-[var(--primary)]",children:v(r)}):null]},r.id))})]},c))})]})]})}export{Z as ActivityPage};