forge-openclaw-plugin 0.2.109 → 0.2.111
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/{activity-page-DWHZ8_sJ.js → activity-page-BdeoV3N4.js} +1 -1
- package/dist/assets/{ai-surface-workspace-DPygfyhc.js → ai-surface-workspace-BCbQUekv.js} +1 -1
- package/dist/assets/{atlas-panel-D261RUQF.js → atlas-panel-DTnRBOZx.js} +1 -1
- package/dist/assets/{calendar-page-BQbJkdPF.js → calendar-page-YDexXy_e.js} +1 -1
- package/dist/assets/{calendar-rules-BoFsuvZ8.js → calendar-rules-B_aOcMi3.js} +1 -1
- package/dist/assets/{calendar-week-toolbar-LetevPI7.js → calendar-week-toolbar-BTbIzq3e.js} +1 -1
- package/dist/assets/{companion-sync-lab-page-Bid-4B8W.js → companion-sync-lab-page-DKouMut9.js} +1 -1
- package/dist/assets/{daily-metrics-dashboard-CqnGJNeI.js → daily-metrics-dashboard-B3UCGG1t.js} +1 -1
- package/dist/assets/{entity-note-count-link-VMURwBMi.js → entity-note-count-link-DUxobQUt.js} +1 -1
- package/dist/assets/{entity-notes-surface-DGXcyNzo.js → entity-notes-surface-BdHBn8rx.js} +1 -1
- package/dist/assets/{execution-board-BGf6zZqJ.js → execution-board-R5-oyf-Z.js} +1 -1
- package/dist/assets/{faceted-token-search-CAsZrhZ4.js → faceted-token-search-DtO6OUwQ.js} +1 -1
- package/dist/assets/{flagship-signal-deck-DJsq3jn2.js → flagship-signal-deck-B9k46sWA.js} +1 -1
- package/dist/assets/{floating-action-menu-B3tQStUN.js → floating-action-menu-DBzfnJOx.js} +1 -1
- package/dist/assets/{goal-detail-page-DBs-9wpk.js → goal-detail-page-Mr_gnZ2l.js} +1 -1
- package/dist/assets/{goals-page-CPmhjsG-.js → goals-page-BG7FuqK1.js} +1 -1
- package/dist/assets/{habits-page-DkW8zT6_.js → habits-page-CGdaOqL3.js} +1 -1
- package/dist/assets/{index-BhkubYaC.js → index-BP9__l6C.js} +2 -2
- package/dist/assets/{insight-flow-dialog-BvgRjkPi.js → insight-flow-dialog-BUv06Vn-.js} +1 -1
- package/dist/assets/{insights-page-Dtij6pm5.js → insights-page-BgD1_1we.js} +1 -1
- package/dist/assets/{kanban-page-R1x5hUU-.js → kanban-page-DvZSN-Bi.js} +1 -1
- package/dist/assets/{knowledge-graph-page-BRBB5Vvz.js → knowledge-graph-page-BXWXtPvo.js} +1 -1
- package/dist/assets/{life-force-page-DGPWKM9I.js → life-force-page-CwjHvpzu.js} +1 -1
- package/dist/assets/{life-force-workspace-DoRIQoU_.js → life-force-workspace-Ct70SsNQ.js} +1 -1
- package/dist/assets/{metric-tile-NJiFcFxW.js → metric-tile-DCxLxNli.js} +1 -1
- package/dist/assets/{movement-page-Dk8aV737.js → movement-page-C06ebiYk.js} +1 -1
- package/dist/assets/{note-markdown-CZHjJDve.js → note-markdown-3wB52NOn.js} +1 -1
- package/dist/assets/{note-tags-input-DFpxZHPg.js → note-tags-input-Blg5Gqz_.js} +1 -1
- package/dist/assets/{notes-page-r3kymlqg.js → notes-page-CSkjizyK.js} +1 -1
- package/dist/assets/{open-in-graph-button-B_XHtHZD.js → open-in-graph-button-CDHxzS8B.js} +1 -1
- package/dist/assets/{orbit-map-C_xy4kYC.js → orbit-map-OV-GcUZl.js} +1 -1
- package/dist/assets/{overview-page-Dwg4uYe9.js → overview-page-Cw3pI34n.js} +1 -1
- package/dist/assets/{page-hero-DzEsy8i5.js → page-hero-DgnhI5es.js} +1 -1
- package/dist/assets/{pill-cluster-DcBUeEMT.js → pill-cluster-DIGkIvKN.js} +1 -1
- package/dist/assets/{preference-entity-handoff-button-BX2n07ob.js → preference-entity-handoff-button-LT_GBuqB.js} +1 -1
- package/dist/assets/{preferences-page-BSnwiWBt.js → preferences-page-Dkvz7nkU.js} +1 -1
- package/dist/assets/{project-collections-BR6YdlCZ.js → project-collections-BsAT8WYE.js} +1 -1
- package/dist/assets/{project-detail-page-DeYshcCb.js → project-detail-page-DsJUWg8k.js} +1 -1
- package/dist/assets/{project-management-hierarchy-page-Bhdt79Ea.js → project-management-hierarchy-page-CIy0wAp8.js} +1 -1
- package/dist/assets/{project-management-section-nav-DnXuWUfe.js → project-management-section-nav-ClB2S1FW.js} +1 -1
- package/dist/assets/{projects-page-BA9W0ZPk.js → projects-page-Da1bZLSH.js} +1 -1
- package/dist/assets/{psyche-behaviors-page-DpTj53GI.js → psyche-behaviors-page-D0uwf78o.js} +1 -1
- package/dist/assets/{psyche-flashcards-page-CV58hAJA.js → psyche-flashcards-page-e_qwwsOf.js} +1 -1
- package/dist/assets/{psyche-goal-map-page-C07CALFg.js → psyche-goal-map-page-BIY028Wx.js} +1 -1
- package/dist/assets/{psyche-graph-CP7mxfMP.js → psyche-graph-Y1UKALEm.js} +1 -1
- package/dist/assets/{psyche-metrics-page-CY8e9R0D.js → psyche-metrics-page-DDkGJzo_.js} +1 -1
- package/dist/assets/{psyche-mode-guide-page-DYt6AmHl.js → psyche-mode-guide-page-C7mD9uiv.js} +1 -1
- package/dist/assets/{psyche-modes-page-Dd77kTtJ.js → psyche-modes-page-RUdaOQWr.js} +1 -1
- package/dist/assets/{psyche-page-DNXZjIKp.js → psyche-page-D3FZy__c.js} +1 -1
- package/dist/assets/{psyche-patterns-page-DxGzpeT5.js → psyche-patterns-page-BSD35F41.js} +1 -1
- package/dist/assets/{psyche-questionnaire-builder-page-5EQctRHO.js → psyche-questionnaire-builder-page-BpgUTMgj.js} +1 -1
- package/dist/assets/{psyche-questionnaire-detail-page-W8nhUGAc.js → psyche-questionnaire-detail-page-DwjyjNhg.js} +1 -1
- package/dist/assets/{psyche-questionnaire-run-detail-page-DVwzSuqg.js → psyche-questionnaire-run-detail-page-mJ-DgeB-.js} +1 -1
- package/dist/assets/{psyche-questionnaire-run-page-BHResQgL.js → psyche-questionnaire-run-page-YuMBkH1G.js} +1 -1
- package/dist/assets/{psyche-questionnaires-page-DFTOXpyp.js → psyche-questionnaires-page-CNglToKh.js} +1 -1
- package/dist/assets/{psyche-report-detail-page-BqDE_tf7.js → psyche-report-detail-page-CV6tbLxz.js} +1 -1
- package/dist/assets/{psyche-reports-page-B75pDz0l.js → psyche-reports-page-BEOe8OPA.js} +1 -1
- package/dist/assets/{psyche-schemas-beliefs-page-BjSO4tYL.js → psyche-schemas-beliefs-page-KIRqmO8C.js} +1 -1
- package/dist/assets/{psyche-screen-time-page-B7lKf1g7.js → psyche-screen-time-page-DvXuwgU1.js} +1 -1
- package/dist/assets/{psyche-self-observation-page-C6RMThwc.js → psyche-self-observation-page-E6yDPBm1.js} +1 -1
- package/dist/assets/{psyche-values-page-1O158mIJ.js → psyche-values-page-CSLG7C6a.js} +1 -1
- package/dist/assets/{report-chain-fields-CyJ_VFY5.js → report-chain-fields-BYEp3tRr.js} +1 -1
- package/dist/assets/{rewards-page-sPlDYuVo.js → rewards-page-pAa_MIte.js} +1 -1
- package/dist/assets/{scheduling-rules-editor-DGgcBs1W.js → scheduling-rules-editor-BmGrjC9q.js} +1 -1
- package/dist/assets/{schema-badge-Do-YvNHm.js → schema-badge-IVJ4SoeC.js} +1 -1
- package/dist/assets/{select-menu-CAM7rMdO.js → select-menu-BE5AIdgO.js} +1 -1
- package/dist/assets/{settings-agents-page-BsZY71kz.js → settings-agents-page-hqI0IaNx.js} +1 -1
- package/dist/assets/{settings-bin-page-kB3jtHo_.js → settings-bin-page-2bPjFtZ3.js} +1 -1
- package/dist/assets/{settings-calendar-page-C-IGNk3_.js → settings-calendar-page-u_TfZJEh.js} +1 -1
- package/dist/assets/{settings-data-page-CFGSL9g3.js → settings-data-page-DSQ14F2i.js} +1 -1
- package/dist/assets/{settings-logs-page-C7e9FIN_.js → settings-logs-page-BU0CJhDM.js} +1 -1
- package/dist/assets/{settings-mobile-page-DzKeZQDM.js → settings-mobile-page-B1fQ61BB.js} +1 -1
- package/dist/assets/{settings-models-page-dFhaOV3E.js → settings-models-page-B0Dis_c_.js} +1 -1
- package/dist/assets/{settings-page-C0fSyYUx.js → settings-page-V4ibXIp9.js} +1 -1
- package/dist/assets/{settings-rewards-page-D-LuZHhv.js → settings-rewards-page-DeCGBNn5.js} +1 -1
- package/dist/assets/{settings-section-nav-kFFQSJEe.js → settings-section-nav-DV5umEkp.js} +1 -1
- package/dist/assets/{settings-users-page-CX797HPB.js → settings-users-page-BB8Dzd_P.js} +1 -1
- package/dist/assets/{settings-wiki-page-CWLAvcsf.js → settings-wiki-page-B0k0KRsU.js} +1 -1
- package/dist/assets/{sleep-page-QgBkKyk_.js → sleep-page-CPwUb-em.js} +1 -1
- package/dist/assets/{sports-page-C8tLMl7V.js → sports-page-Cl4vZVwh.js} +1 -1
- package/dist/assets/{strategies-page-CGgh-KXu.js → strategies-page-DhFXf-g3.js} +1 -1
- package/dist/assets/{strategy-detail-page-CnzqjjoD.js → strategy-detail-page-B2gotRKI.js} +1 -1
- package/dist/assets/{strategy-dialog-C6AThZ9L.js → strategy-dialog-DD1bbrRB.js} +1 -1
- package/dist/assets/{surface-CIDhCEsF.js → surface-98myBzix.js} +1 -1
- package/dist/assets/{task-detail-page-Dp0r_qZP.js → task-detail-page-nx4eQbMW.js} +1 -1
- package/dist/assets/{timebox-planning-dialog-B3uaSsWI.js → timebox-planning-dialog-DoZ9zUra.js} +1 -1
- package/dist/assets/{today-page-CTnMYrPi.js → today-page-7BH7Yne1.js} +1 -1
- package/dist/assets/{training-load-page-D0WB5_Tq.js → training-load-page-E82pNn1-.js} +1 -1
- package/dist/assets/{vitals-page-0vg8nqwj.js → vitals-page-D64WlwVv.js} +1 -1
- package/dist/assets/{weekly-review-page-CSSKlkbH.js → weekly-review-page-BkRxxm7Q.js} +1 -1
- package/dist/assets/weight-loss-page-DPbg7rTC.js +5 -0
- package/dist/assets/{wiki-article-markdown-CBFvfJx_.js → wiki-article-markdown-B0Q6082A.js} +1 -1
- package/dist/assets/{wiki-editor-page-CL1uxpih.js → wiki-editor-page-Tkb50wEA.js} +1 -1
- package/dist/assets/{wiki-ingest-history-page-XX8DeNCC.js → wiki-ingest-history-page-CcQzkZRr.js} +1 -1
- package/dist/assets/{wiki-ingest-modal-j-ftDmzp.js → wiki-ingest-modal-D6yft0Qb.js} +1 -1
- package/dist/assets/{wiki-page-B46TQYpy.js → wiki-page-DEND1jmD.js} +1 -1
- package/dist/assets/{workbench-flow-page-CicEuBKq.js → workbench-flow-page-CkNI03j_.js} +1 -1
- package/dist/assets/{workbench-page-tXezYLh_.js → workbench-page-W3zQpeWr.js} +1 -1
- package/dist/assets/{workout-detail-page-BTO5X0Of.js → workout-detail-page-BVSZv53z.js} +1 -1
- package/dist/index.html +1 -1
- package/dist/server/server/src/app.js +2 -0
- package/dist/server/server/src/health-weight-loss.js +160 -10
- package/dist/server/src/lib/weight-loss-energy-model.js +64 -0
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/skills/forge-openclaw/SKILL.md +10 -0
- package/skills/forge-openclaw/entity_conversation_playbooks.md +33 -0
- package/skills/forge-openclaw/psyche_entity_playbooks.md +31 -0
- package/dist/assets/weight-loss-page-Dcr4wjjy.js +0 -5
|
@@ -4,6 +4,7 @@ import { getDatabase, runInTransaction } from "./db.js";
|
|
|
4
4
|
import { getSettings } from "./repositories/settings.js";
|
|
5
5
|
import { getDefaultUser, resolveUserForMutation } from "./repositories/users.js";
|
|
6
6
|
import { readEncryptedSecret } from "./repositories/calendar.js";
|
|
7
|
+
import { hallNiddkWeeklyRateKgToDailyEnergyAdjustment } from "../../src/lib/weight-loss-energy-model.js";
|
|
7
8
|
const optionalNumberSchema = z
|
|
8
9
|
.union([z.coerce.number().finite(), z.null()])
|
|
9
10
|
.optional();
|
|
@@ -269,12 +270,31 @@ function average(values) {
|
|
|
269
270
|
}
|
|
270
271
|
return real.reduce((sum, value) => sum + value, 0) / real.length;
|
|
271
272
|
}
|
|
272
|
-
function
|
|
273
|
+
function median(values) {
|
|
274
|
+
const sorted = values
|
|
275
|
+
.filter((value) => Number.isFinite(value))
|
|
276
|
+
.sort((left, right) => left - right);
|
|
277
|
+
if (sorted.length === 0) {
|
|
278
|
+
return null;
|
|
279
|
+
}
|
|
280
|
+
const middle = Math.floor(sorted.length / 2);
|
|
281
|
+
if (sorted.length % 2 === 1) {
|
|
282
|
+
return sorted[middle] ?? null;
|
|
283
|
+
}
|
|
284
|
+
return ((sorted[middle - 1] ?? 0) + (sorted[middle] ?? 0)) / 2;
|
|
285
|
+
}
|
|
286
|
+
function metricRecord(metrics, key) {
|
|
273
287
|
const metric = metrics[key];
|
|
274
288
|
if (!metric || typeof metric !== "object") {
|
|
275
289
|
return null;
|
|
276
290
|
}
|
|
277
|
-
|
|
291
|
+
return metric;
|
|
292
|
+
}
|
|
293
|
+
function metricTotal(metrics, key) {
|
|
294
|
+
const record = metricRecord(metrics, key);
|
|
295
|
+
if (!record) {
|
|
296
|
+
return null;
|
|
297
|
+
}
|
|
278
298
|
for (const field of ["total", "average", "latest"]) {
|
|
279
299
|
const value = record[field];
|
|
280
300
|
if (typeof value === "number" && Number.isFinite(value)) {
|
|
@@ -283,6 +303,31 @@ function metricTotal(metrics, key) {
|
|
|
283
303
|
}
|
|
284
304
|
return null;
|
|
285
305
|
}
|
|
306
|
+
function metricSampleCount(metrics, key) {
|
|
307
|
+
const value = metricRecord(metrics, key)?.sampleCount;
|
|
308
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
309
|
+
}
|
|
310
|
+
function metricLatestSampleAt(metrics, key) {
|
|
311
|
+
const value = metricRecord(metrics, key)?.latestSampleAt;
|
|
312
|
+
return typeof value === "string" && value.trim().length > 0
|
|
313
|
+
? value.trim()
|
|
314
|
+
: null;
|
|
315
|
+
}
|
|
316
|
+
function sampleHourOfDay(value) {
|
|
317
|
+
if (!value) {
|
|
318
|
+
return null;
|
|
319
|
+
}
|
|
320
|
+
const match = value.match(/T(\d{2}):/);
|
|
321
|
+
if (match?.[1]) {
|
|
322
|
+
const parsed = Number(match[1]);
|
|
323
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
324
|
+
}
|
|
325
|
+
const date = new Date(value);
|
|
326
|
+
if (Number.isNaN(date.getTime())) {
|
|
327
|
+
return null;
|
|
328
|
+
}
|
|
329
|
+
return date.getHours();
|
|
330
|
+
}
|
|
286
331
|
function parsePlanNoteNumber(notes, key) {
|
|
287
332
|
if (!notes) {
|
|
288
333
|
return null;
|
|
@@ -292,6 +337,30 @@ function parsePlanNoteNumber(notes, key) {
|
|
|
292
337
|
const parsed = Number(match?.[1]?.trim());
|
|
293
338
|
return Number.isFinite(parsed) ? parsed : null;
|
|
294
339
|
}
|
|
340
|
+
function parsePlanNoteText(notes, key) {
|
|
341
|
+
if (!notes) {
|
|
342
|
+
return null;
|
|
343
|
+
}
|
|
344
|
+
const escapedKey = key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
345
|
+
const match = notes.match(new RegExp(`${escapedKey}=([^;]+)`));
|
|
346
|
+
return match?.[1]?.trim() ?? null;
|
|
347
|
+
}
|
|
348
|
+
function mifflinStJeorRestingKcal(input) {
|
|
349
|
+
if ((input.sex !== "male" && input.sex !== "female") ||
|
|
350
|
+
input.ageYears == null ||
|
|
351
|
+
input.ageYears <= 0 ||
|
|
352
|
+
input.heightCm == null ||
|
|
353
|
+
input.heightCm <= 0 ||
|
|
354
|
+
input.weightKg == null ||
|
|
355
|
+
input.weightKg <= 0) {
|
|
356
|
+
return null;
|
|
357
|
+
}
|
|
358
|
+
const sexAdjustment = input.sex === "male" ? 5 : -161;
|
|
359
|
+
return (10 * input.weightKg +
|
|
360
|
+
6.25 * input.heightCm -
|
|
361
|
+
5 * input.ageYears +
|
|
362
|
+
sexAdjustment);
|
|
363
|
+
}
|
|
295
364
|
function estimateStepActiveCaloriesKcal(input) {
|
|
296
365
|
if (input.stepCount == null ||
|
|
297
366
|
input.stepCount <= 0 ||
|
|
@@ -344,6 +413,8 @@ function buildStoredEnergyModel(input) {
|
|
|
344
413
|
dateKey: row.date_key,
|
|
345
414
|
activeEnergyKcal: metricTotal(metrics, "activeEnergyBurned"),
|
|
346
415
|
restingEnergyKcal: metricTotal(metrics, "basalEnergyBurned"),
|
|
416
|
+
restingSampleCount: metricSampleCount(metrics, "basalEnergyBurned"),
|
|
417
|
+
restingLatestSampleAt: metricLatestSampleAt(metrics, "basalEnergyBurned"),
|
|
347
418
|
exerciseMinutes: metricTotal(metrics, "appleExerciseTime"),
|
|
348
419
|
stepCount: metricTotal(metrics, "stepCount")
|
|
349
420
|
};
|
|
@@ -396,7 +467,59 @@ function buildStoredEnergyModel(input) {
|
|
|
396
467
|
.get(input.userId, input.dayStartAt, input.dayEndAt)
|
|
397
468
|
: null;
|
|
398
469
|
const activeEnergyAverage = average(dailyHealthKit.map((day) => day.activeEnergyKcal));
|
|
399
|
-
const
|
|
470
|
+
const restingEvidenceDays = dailyHealthKit.filter((day) => day.restingEnergyKcal != null);
|
|
471
|
+
const restingExclusionReasons = [];
|
|
472
|
+
const qualifiedRestingDays = restingEvidenceDays.filter((day) => {
|
|
473
|
+
const reasons = [];
|
|
474
|
+
if (day.dateKey >= todayKey) {
|
|
475
|
+
reasons.push("current_day_or_future_day");
|
|
476
|
+
}
|
|
477
|
+
if ((day.restingSampleCount ?? 0) < 24) {
|
|
478
|
+
reasons.push(`sample_count_${day.restingSampleCount ?? 0}_below_24`);
|
|
479
|
+
}
|
|
480
|
+
const latestSampleHour = sampleHourOfDay(day.restingLatestSampleAt);
|
|
481
|
+
if (latestSampleHour == null) {
|
|
482
|
+
reasons.push("missing_latest_basal_sample");
|
|
483
|
+
}
|
|
484
|
+
else if (latestSampleHour < 20) {
|
|
485
|
+
reasons.push(`latest_basal_sample_hour_${latestSampleHour}_before_20`);
|
|
486
|
+
}
|
|
487
|
+
if (input.formulaRestingKcal != null && day.restingEnergyKcal != null) {
|
|
488
|
+
const lowThreshold = Math.max(input.formulaRestingKcal * 0.75, 1000);
|
|
489
|
+
if (day.restingEnergyKcal < lowThreshold) {
|
|
490
|
+
reasons.push(`basal_${round(day.restingEnergyKcal, 0)}_below_${round(lowThreshold, 0)}`);
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
if (reasons.length > 0) {
|
|
494
|
+
restingExclusionReasons.push(`${day.dateKey}: ${reasons.join(",")}`);
|
|
495
|
+
return false;
|
|
496
|
+
}
|
|
497
|
+
return true;
|
|
498
|
+
});
|
|
499
|
+
const wearableRestingKcal = median(qualifiedRestingDays
|
|
500
|
+
.map((day) => day.restingEnergyKcal)
|
|
501
|
+
.filter((value) => value != null));
|
|
502
|
+
const chosenRestingKcal = input.formulaRestingKcal ?? input.savedRestingKcal ?? null;
|
|
503
|
+
const chosenRestingSource = input.formulaRestingKcal != null
|
|
504
|
+
? "mifflin_profile_baseline"
|
|
505
|
+
: input.savedRestingKcal != null
|
|
506
|
+
? "saved_plan_legacy"
|
|
507
|
+
: null;
|
|
508
|
+
const restingConfidence = (() => {
|
|
509
|
+
if (input.formulaRestingKcal == null && input.savedRestingKcal != null) {
|
|
510
|
+
return "low";
|
|
511
|
+
}
|
|
512
|
+
if (input.formulaRestingKcal == null) {
|
|
513
|
+
return "low";
|
|
514
|
+
}
|
|
515
|
+
if (wearableRestingKcal != null &&
|
|
516
|
+
Math.abs(wearableRestingKcal - input.formulaRestingKcal) /
|
|
517
|
+
input.formulaRestingKcal >
|
|
518
|
+
0.15) {
|
|
519
|
+
return "review";
|
|
520
|
+
}
|
|
521
|
+
return qualifiedRestingDays.length > 0 ? "high" : "medium";
|
|
522
|
+
})();
|
|
400
523
|
const workoutEnergyAverage = average([...workoutByDay.values()]);
|
|
401
524
|
const movementCaloriesAverage = average([...movementByDay.values()]);
|
|
402
525
|
const fallbackActiveBurn = workoutEnergyAverage != null || movementCaloriesAverage != null
|
|
@@ -477,13 +600,15 @@ function buildStoredEnergyModel(input) {
|
|
|
477
600
|
const todayActiveCalories = input.dailyActiveOverride?.activeCaloriesKcal ??
|
|
478
601
|
todayObservedActiveCalories ??
|
|
479
602
|
baselineActiveCalories;
|
|
480
|
-
const
|
|
481
|
-
const
|
|
482
|
-
|
|
603
|
+
const todayActiveSurplusKcal = Math.max(0, todayActiveCalories - baselineActiveCalories);
|
|
604
|
+
const todayActivityBufferKcal = todayActiveSurplusKcal * input.activityEatBackFraction;
|
|
605
|
+
const todayTargetAdjustmentKcal = todayActivityBufferKcal;
|
|
606
|
+
const estimatedTdeeKcal = activeBurnKcal != null && chosenRestingKcal != null
|
|
607
|
+
? round(activeBurnKcal + chosenRestingKcal, 0)
|
|
483
608
|
: input.inferredTdee;
|
|
484
609
|
const hasHealthKitDailyActiveEnergy = activeEnergyAverage != null;
|
|
485
610
|
const hasHealthKitEnergy = hasHealthKitDailyActiveEnergy ||
|
|
486
|
-
|
|
611
|
+
wearableRestingKcal != null ||
|
|
487
612
|
workoutEnergyAverage != null;
|
|
488
613
|
const hasMovementEnergy = movementCaloriesAverage != null;
|
|
489
614
|
const sourceConfidence = activeEnergyAverage != null
|
|
@@ -493,7 +618,18 @@ function buildStoredEnergyModel(input) {
|
|
|
493
618
|
: "target_inference_only";
|
|
494
619
|
return {
|
|
495
620
|
activeEnergyCalories: activeEnergyAverage != null ? round(activeEnergyAverage, 0) : null,
|
|
496
|
-
restingEnergyCalories:
|
|
621
|
+
restingEnergyCalories: chosenRestingKcal != null ? round(chosenRestingKcal, 0) : null,
|
|
622
|
+
formulaRestingKcal: input.formulaRestingKcal != null
|
|
623
|
+
? round(input.formulaRestingKcal, 0)
|
|
624
|
+
: null,
|
|
625
|
+
wearableRestingKcal: wearableRestingKcal != null ? round(wearableRestingKcal, 0) : null,
|
|
626
|
+
wearableRestingSource: wearableRestingKcal != null ? "healthkit_basal_energy" : null,
|
|
627
|
+
wearableRestingDayCount: restingEvidenceDays.length,
|
|
628
|
+
wearableRestingCoverageQualifiedDayCount: qualifiedRestingDays.length,
|
|
629
|
+
chosenRestingKcal: chosenRestingKcal != null ? round(chosenRestingKcal, 0) : null,
|
|
630
|
+
chosenRestingSource,
|
|
631
|
+
restingConfidence,
|
|
632
|
+
restingExclusionReasons,
|
|
497
633
|
wearableConfidence: hasHealthKitEnergy
|
|
498
634
|
? "measured_directional"
|
|
499
635
|
: "directional",
|
|
@@ -507,6 +643,9 @@ function buildStoredEnergyModel(input) {
|
|
|
507
643
|
: null,
|
|
508
644
|
todayActiveCaloriesSource: todayActiveSource,
|
|
509
645
|
todayTargetAdjustmentKcal: round(todayTargetAdjustmentKcal, 0),
|
|
646
|
+
todayActiveSurplusKcal: round(todayActiveSurplusKcal, 0),
|
|
647
|
+
todayActivityBufferKcal: round(todayActivityBufferKcal, 0),
|
|
648
|
+
activityEatBackFraction: round(input.activityEatBackFraction, 2),
|
|
510
649
|
todayWorkoutEnergyKcal: todayWorkoutEnergy != null ? round(todayWorkoutEnergy, 0) : null,
|
|
511
650
|
todayMovementCaloriesKcal: todayMovementCalories != null ? round(todayMovementCalories, 0) : null,
|
|
512
651
|
todayHealthKitActiveCaloriesKcal: todayHealthKitActive != null ? round(todayHealthKitActive, 0) : null,
|
|
@@ -1539,9 +1678,17 @@ export function getWeightLossViewData(userIds, options = {}) {
|
|
|
1539
1678
|
? round(recentTotals.calories / recentTrackedDays, 0)
|
|
1540
1679
|
: 0;
|
|
1541
1680
|
const inferredTdee = target.calorieTarget != null
|
|
1542
|
-
? round(target.calorieTarget +
|
|
1681
|
+
? round(target.calorieTarget +
|
|
1682
|
+
Math.abs(hallNiddkWeeklyRateKgToDailyEnergyAdjustment(target.weeklyRateGoalKg) ?? 0), 0)
|
|
1543
1683
|
: null;
|
|
1544
1684
|
const defaultActiveCalories = parsePlanNoteNumber(target.notes, "activity_kcal");
|
|
1685
|
+
const formulaRestingKcal = mifflinStJeorRestingKcal({
|
|
1686
|
+
sex: parsePlanNoteText(target.notes, "sex"),
|
|
1687
|
+
ageYears: parsePlanNoteNumber(target.notes, "age_years"),
|
|
1688
|
+
heightCm: parsePlanNoteNumber(target.notes, "height_cm"),
|
|
1689
|
+
weightKg: weightTrend.latestWeightKg
|
|
1690
|
+
});
|
|
1691
|
+
const activityEatBackFraction = Math.min(1, Math.max(0, parsePlanNoteNumber(target.notes, "eat_back_fraction") ?? 0.5));
|
|
1545
1692
|
const dailyActiveOverride = getDailyEnergyOverride(userId, todayKey);
|
|
1546
1693
|
const energyModel = buildStoredEnergyModel({
|
|
1547
1694
|
userId,
|
|
@@ -1554,7 +1701,10 @@ export function getWeightLossViewData(userIds, options = {}) {
|
|
|
1554
1701
|
recentFoodLogDayCount: recentTrackedDays,
|
|
1555
1702
|
defaultActiveCalories,
|
|
1556
1703
|
latestWeightKg: weightTrend.latestWeightKg,
|
|
1557
|
-
dailyActiveOverride
|
|
1704
|
+
dailyActiveOverride,
|
|
1705
|
+
formulaRestingKcal,
|
|
1706
|
+
savedRestingKcal: parsePlanNoteNumber(target.notes, "resting_kcal"),
|
|
1707
|
+
activityEatBackFraction
|
|
1558
1708
|
});
|
|
1559
1709
|
const todayTargetCalories = Math.max(0, round(target.calorieTarget + energyModel.todayTargetAdjustmentKcal, 0));
|
|
1560
1710
|
const todayLedger = buildTodayLedger(logs, target, todayKey, todayTargetCalories, energyModel.todayTargetAdjustmentKcal, energyModel.todayActiveCaloriesSource);
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
export const HALL_NIDDK_LINEAR_WEIGHT_MODEL = {
|
|
2
|
+
id: "hall_niddk_linearized_adult_12w",
|
|
3
|
+
label: "Hall/NIDDK linearized adult model",
|
|
4
|
+
defaultPlanningHorizonDays: 84,
|
|
5
|
+
steadyStateKcalPerKgPerDay: 10 / 0.45359237,
|
|
6
|
+
timeConstantDays: 365,
|
|
7
|
+
staticEnergyDensityKcalPerKg: 7700
|
|
8
|
+
};
|
|
9
|
+
function positiveFinite(value) {
|
|
10
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0
|
|
11
|
+
? value
|
|
12
|
+
: null;
|
|
13
|
+
}
|
|
14
|
+
export function hallNiddkWeightModelParameters(options = {}) {
|
|
15
|
+
const planningHorizonDays = positiveFinite(options.planningHorizonDays) ??
|
|
16
|
+
HALL_NIDDK_LINEAR_WEIGHT_MODEL.defaultPlanningHorizonDays;
|
|
17
|
+
const timeConstantDays = positiveFinite(options.timeConstantDays) ??
|
|
18
|
+
HALL_NIDDK_LINEAR_WEIGHT_MODEL.timeConstantDays;
|
|
19
|
+
const steadyStateKcalPerKgPerDay = positiveFinite(options.steadyStateKcalPerKgPerDay) ??
|
|
20
|
+
HALL_NIDDK_LINEAR_WEIGHT_MODEL.steadyStateKcalPerKgPerDay;
|
|
21
|
+
const responseFraction = 1 - Math.exp(-planningHorizonDays / timeConstantDays);
|
|
22
|
+
return {
|
|
23
|
+
planningHorizonDays,
|
|
24
|
+
timeConstantDays,
|
|
25
|
+
steadyStateKcalPerKgPerDay,
|
|
26
|
+
responseFraction
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
export function hallNiddkWeeklyRateKgToDailyEnergyAdjustment(weeklyRateKg, options = {}) {
|
|
30
|
+
if (typeof weeklyRateKg !== "number" || !Number.isFinite(weeklyRateKg)) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
if (weeklyRateKg === 0) {
|
|
34
|
+
return 0;
|
|
35
|
+
}
|
|
36
|
+
const parameters = hallNiddkWeightModelParameters(options);
|
|
37
|
+
if (parameters.responseFraction <= 0) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
const targetChangeKg = weeklyRateKg * (parameters.planningHorizonDays / 7);
|
|
41
|
+
return ((targetChangeKg * parameters.steadyStateKcalPerKgPerDay) /
|
|
42
|
+
parameters.responseFraction);
|
|
43
|
+
}
|
|
44
|
+
export function hallNiddkDailyEnergyAdjustmentToAverageWeeklyRateKg(dailyEnergyAdjustmentKcal, options = {}) {
|
|
45
|
+
if (typeof dailyEnergyAdjustmentKcal !== "number" ||
|
|
46
|
+
!Number.isFinite(dailyEnergyAdjustmentKcal)) {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
if (dailyEnergyAdjustmentKcal === 0) {
|
|
50
|
+
return 0;
|
|
51
|
+
}
|
|
52
|
+
const parameters = hallNiddkWeightModelParameters(options);
|
|
53
|
+
const totalChangeKg = (dailyEnergyAdjustmentKcal / parameters.steadyStateKcalPerKgPerDay) *
|
|
54
|
+
parameters.responseFraction;
|
|
55
|
+
return totalChangeKg / (parameters.planningHorizonDays / 7);
|
|
56
|
+
}
|
|
57
|
+
export function staticKgRateToDailyEnergyAdjustment(weeklyRateKg) {
|
|
58
|
+
if (typeof weeklyRateKg !== "number" || !Number.isFinite(weeklyRateKg)) {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
return ((weeklyRateKg *
|
|
62
|
+
HALL_NIDDK_LINEAR_WEIGHT_MODEL.staticEnergyDensityKcalPerKg) /
|
|
63
|
+
7);
|
|
64
|
+
}
|
package/openclaw.plugin.json
CHANGED
|
@@ -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.
|
|
5
|
+
"version": "0.2.111",
|
|
6
6
|
"activation": {
|
|
7
7
|
"onStartup": true,
|
|
8
8
|
"onCapabilities": [
|
package/package.json
CHANGED
|
@@ -238,6 +238,11 @@ Entity conversation rule:
|
|
|
238
238
|
|
|
239
239
|
- For all entity creation or update flows, first use [`entity_conversation_playbooks.md`](./entity_conversation_playbooks.md) to decide the next best question.
|
|
240
240
|
- Ask only for what is missing or unclear. Do not walk through every schema field.
|
|
241
|
+
- Before asking another question, run the playbook's minimum save-readiness
|
|
242
|
+
checkpoint: if accepted wording, meaningful body, route lane, target object or
|
|
243
|
+
time scope, and any ownership/placement that changes later use are already clear,
|
|
244
|
+
summarize once and write, read, run, or update instead of collecting optional
|
|
245
|
+
fields.
|
|
241
246
|
- Let each question have one job. Know what you are trying to clarify before you ask it.
|
|
242
247
|
- Before you ask, decide the exact missing thing you need and how that answer will help you name, place, or save the record.
|
|
243
248
|
- Prefer a progression of:
|
|
@@ -299,6 +304,11 @@ Psyche interview rule:
|
|
|
299
304
|
- In that first exploratory turn, ask only one question, do not search Forge or mention whether a matching entity exists, and avoid openings like "This sounds like" or "What you're describing is".
|
|
300
305
|
- In that first exploratory turn, prefer exactly two sentences: one brief empathic reflection and one concrete question. Avoid colons because they tend to trigger list-like answers.
|
|
301
306
|
- Follow the preferred opening-question patterns in [`psyche_entity_playbooks.md`](./psyche_entity_playbooks.md) when they fit the entity the user is exploring.
|
|
307
|
+
- After a Psyche formulation lands, use the Psyche save-readiness checkpoint from
|
|
308
|
+
the playbook. If the belief sentence, functional loop, behavior move, part-state,
|
|
309
|
+
trigger episode, value, event type, emotion definition, or flashcard cue/message is
|
|
310
|
+
true enough to save, ask at most one accuracy question and then use shared batch
|
|
311
|
+
CRUD.
|
|
302
312
|
- If the user already offers a usable belief sentence, value phrase, or mode name,
|
|
303
313
|
refine from their wording first instead of replacing it with a cleaner label too
|
|
304
314
|
early.
|
|
@@ -258,6 +258,39 @@ Most good Forge intake flows follow this sequence:
|
|
|
258
258
|
|
|
259
259
|
That sequence is not a script. Skip steps the user already answered.
|
|
260
260
|
|
|
261
|
+
## Minimum save-readiness checkpoint
|
|
262
|
+
|
|
263
|
+
Use this before asking another polished follow-up. The question quality is worse, not
|
|
264
|
+
better, when the agent keeps exploring after the record or route is already clear
|
|
265
|
+
enough to act.
|
|
266
|
+
|
|
267
|
+
- For normal batch entities, save or update when you have the accepted working name
|
|
268
|
+
or distinctive wording, the meaningful body of the record, and owner scope only if
|
|
269
|
+
ownership changes accountability. Do not ask for tags, links, dates, priority,
|
|
270
|
+
assignees, or status just because those fields exist.
|
|
271
|
+
- For strategic and planning records, the minimum is the intended outcome plus the
|
|
272
|
+
hierarchy placement that would change later retrieval or execution. If placement is
|
|
273
|
+
not known but the user asked to capture the idea now, save the provisional record
|
|
274
|
+
with clear wording and leave placement for a later link/update.
|
|
275
|
+
- For operational records, the minimum is the target action plus the time, object, or
|
|
276
|
+
state that makes the action truthful: event time for a calendar event, recurrence
|
|
277
|
+
for a work block, task and slot for a timebox, task id for a task run, or target
|
|
278
|
+
record and signed minutes for a work adjustment. Generate a plain title yourself
|
|
279
|
+
when the title is obvious.
|
|
280
|
+
- For read-model and review surfaces, the minimum is the practical question plus any
|
|
281
|
+
scope that would change the answer. Once you have that, read the overview instead
|
|
282
|
+
of asking for a preferred report shape.
|
|
283
|
+
- For specialized Movement, Life Force, and Workbench writes, the minimum is the
|
|
284
|
+
selected lane plus the target span/object/weekday/flow/run/node and the intended
|
|
285
|
+
correction or effect. Do not ask a reflective question after the dedicated route
|
|
286
|
+
and write shape are already selected.
|
|
287
|
+
- For reflective non-Psyche records, the minimum is what future review should
|
|
288
|
+
remember and the container that preserves it. If a stronger Psyche container clearly
|
|
289
|
+
emerges, route there; otherwise do not keep deepening just to make the note more
|
|
290
|
+
therapeutic.
|
|
291
|
+
- Close the loop in one sentence before acting: "What seems clear now is..." followed
|
|
292
|
+
by the save, update, read, run, or handoff.
|
|
293
|
+
|
|
261
294
|
## Project-management hierarchy playbook
|
|
262
295
|
|
|
263
296
|
When the conversation is about Forge planning or delivery, preserve this
|
|
@@ -236,6 +236,37 @@ of leaving it as warm reflective prose.
|
|
|
236
236
|
- Save through shared batch entity routes only after the user accepts the working
|
|
237
237
|
wording or explicitly asks to save.
|
|
238
238
|
|
|
239
|
+
## Psyche save-readiness checkpoint
|
|
240
|
+
|
|
241
|
+
Use this before asking another deepening question. Psyche work should feel careful,
|
|
242
|
+
but not endless. Once the user recognizes the working formulation, protect momentum and
|
|
243
|
+
save the record instead of reopening the whole story.
|
|
244
|
+
|
|
245
|
+
- For `belief_entry`, the minimum is the accepted sentence or prediction plus whether
|
|
246
|
+
it is mainly about self, others, world/safety, or outcome when that distinction is
|
|
247
|
+
visible.
|
|
248
|
+
- For `behavior_pattern`, the minimum is a concrete cue or situation, the felt
|
|
249
|
+
meaning/body shift, the behavior or urge, and at least one payoff or cost. A
|
|
250
|
+
preferred response is useful, but do not force it before saving a provisional
|
|
251
|
+
functional analysis.
|
|
252
|
+
- For `behavior`, the minimum is the repeated move, the cue or urge that brings it
|
|
253
|
+
online, and whether the move functions as avoidance, committed action, repair, or
|
|
254
|
+
recovery when that is clear.
|
|
255
|
+
- For `mode_profile`, the minimum is the part's voice or posture, what it is trying
|
|
256
|
+
to protect, and what danger or burden it expects.
|
|
257
|
+
- For `mode_guide_session`, the minimum is the present trigger, a brief summary of
|
|
258
|
+
what the active part seems to want, and the answers gathered so far.
|
|
259
|
+
- For `trigger_report`, the minimum is the situation, felt stake, meaning or thought,
|
|
260
|
+
action/urge, and consequence that make it one emotionally meaningful episode.
|
|
261
|
+
- For `psyche_value`, `event_type`, and `emotion_definition`, the minimum is the
|
|
262
|
+
future use: what this value, recurring moment, or lived feeling label will help
|
|
263
|
+
future reports notice consistently.
|
|
264
|
+
- For `flashcard`, the minimum is the cue or urge sentence and one brief message the
|
|
265
|
+
user can recognize while triggered. Link to beliefs, modes, values, or patterns only
|
|
266
|
+
when the link is already clear or will materially improve retrieval.
|
|
267
|
+
- After the minimum is present, ask one accuracy question at most: "Is this true
|
|
268
|
+
enough to save as a first version?" If yes, save through shared batch CRUD.
|
|
269
|
+
|
|
239
270
|
## Psyche Hypothesis Map
|
|
240
271
|
|
|
241
272
|
Use these shapes after at least one concrete example is clear. The hypothesis should
|