forge-openclaw-plugin 0.2.99 → 0.2.100
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-copy-Bj4h9OcF.js +1 -0
- package/dist/assets/activity-page-5oyCFOns.js +1 -0
- package/dist/assets/ai-surface-workspace-qgk_B57-.js +1 -0
- package/dist/assets/atlas-panel-rfH2qOez.js +1 -0
- package/dist/assets/{board-Ju0h0SeG.js → board-BkDRaMp6.js} +1 -1
- package/dist/assets/calendar-display-preferences-Cid-2RnL.js +1 -0
- package/dist/assets/calendar-page-Bo2iua-a.js +1 -0
- package/dist/assets/calendar-rules-DA1g3QUk.js +1 -0
- package/dist/assets/calendar-ui-Cy1XRwzV.js +1 -0
- package/dist/assets/calendar-week-toolbar-DU1Q4RYj.js +1 -0
- package/dist/assets/charts-P7EVhIog.js +36 -0
- package/dist/assets/companion-sync-lab-page-CosNknOK.js +1 -0
- package/dist/assets/daily-metrics-dashboard-LjuGAB3f.js +1 -0
- package/dist/assets/date-keys-Cj1G3TOn.js +1 -0
- package/dist/assets/entity-links-DwpxhW2H.js +1 -0
- package/dist/assets/entity-note-count-link-BmGDB572.js +1 -0
- package/dist/assets/entity-notes-surface-DgEgicaE.js +1 -0
- package/dist/assets/execution-board-CDRXQB85.js +1 -0
- package/dist/assets/faceted-token-search-CE1YauRd.js +1 -0
- package/dist/assets/flagship-signal-deck-DDds90Gl.js +1 -0
- package/dist/assets/floating-action-menu-CJkI2iFy.js +1 -0
- package/dist/assets/forms-BFlTgZ3W.js +1 -0
- package/dist/assets/goal-detail-page-cJvHaLMQ.js +1 -0
- package/dist/assets/goals-page-f_39hvUV.js +1 -0
- package/dist/assets/graph-BZV40eAE.css +1 -0
- package/dist/assets/graph-D6JLqDbD.js +318 -0
- package/dist/assets/habits-page-DKb96_mj.js +1 -0
- package/dist/assets/health-link-options-Cpx8w7uM.js +1 -0
- package/dist/assets/index-BHTUu_4M.js +19 -0
- package/dist/assets/index-CZbuZQjw.css +1 -0
- package/dist/assets/insight-flow-dialog-pzAzyayN.js +1 -0
- package/dist/assets/insights-page-Dc9oFltJ.js +8 -0
- package/dist/assets/kanban-page-JAxerYh6.js +1 -0
- package/dist/assets/knowledge-graph-page-UQ3skqEi.js +1 -0
- package/dist/assets/life-force-page-BGDbQuVh.js +1 -0
- package/dist/assets/life-force-workspace-B1fYSXRC.js +1 -0
- package/dist/assets/maps-B-YMMjus.css +1 -0
- package/dist/assets/maps-ClgJoCjz.js +803 -0
- package/dist/assets/metric-tile-DX6TclqM.js +1 -0
- package/dist/assets/{motion-DRPJkN3a.js → motion-BeD44FeG.js} +1 -1
- package/dist/assets/movement-page-6HP6nGJx.js +1 -0
- package/dist/assets/note-markdown-DiW2-5d3.js +3 -0
- package/dist/assets/note-tags-input-DDLXf54U.js +1 -0
- package/dist/assets/notes-page-BuguDjhz.js +1 -0
- package/dist/assets/open-in-graph-button-Cg5VrKsC.js +1 -0
- package/dist/assets/orbit-map-GD05-0oS.js +1 -0
- package/dist/assets/overview-page-DuOs2OCB.js +1 -0
- package/dist/assets/page-hero-CQWo1Mm_.js +1 -0
- package/dist/assets/pill-cluster-BJogDRDJ.js +1 -0
- package/dist/assets/preference-entity-handoff-button-D4WAs9pC.js +1 -0
- package/dist/assets/preferences-page-BaJTMU1I.js +1 -0
- package/dist/assets/project-collections-DvaX20q_.js +1 -0
- package/dist/assets/project-detail-page-drPIFZGb.js +1 -0
- package/dist/assets/project-management-hierarchy-page-BUbRXvny.js +1 -0
- package/dist/assets/project-management-section-nav-C2Ud8Zdd.js +1 -0
- package/dist/assets/projects-page-BGzEZUtg.js +1 -0
- package/dist/assets/psyche-behaviors-page-Dmm_Io9D.js +5 -0
- package/dist/assets/psyche-flashcards-page-BgNKJ6QJ.js +1 -0
- package/dist/assets/psyche-goal-map-page-DXJs98Vr.js +1 -0
- package/dist/assets/psyche-graph-CFgs_Bqc.js +1 -0
- package/dist/assets/psyche-metrics-page-zYTJDbyZ.js +1 -0
- package/dist/assets/psyche-mode-guide-page-XPgRfCOf.js +1 -0
- package/dist/assets/psyche-modes-page-B-GA8oRF.js +1 -0
- package/dist/assets/psyche-page--r6a3e1t.js +1 -0
- package/dist/assets/psyche-patterns-page-BM5-3bMm.js +5 -0
- package/dist/assets/psyche-questionnaire-builder-page-CJshQ-mg.js +1 -0
- package/dist/assets/psyche-questionnaire-detail-page-USmR5G5A.js +1 -0
- package/dist/assets/psyche-questionnaire-run-detail-page-D7iBCmTi.js +1 -0
- package/dist/assets/psyche-questionnaire-run-page-Cpil-kDh.js +1 -0
- package/dist/assets/psyche-questionnaires-page-C-_y3VwS.js +1 -0
- package/dist/assets/psyche-report-detail-page--dkSPRaj.js +3 -0
- package/dist/assets/psyche-reports-page-CUaOXmIN.js +1 -0
- package/dist/assets/psyche-schemas-HFmg37Wj.js +1 -0
- package/dist/assets/psyche-schemas-beliefs-page-BX6xaap3.js +9 -0
- package/dist/assets/psyche-screen-time-page-CAAI4mD7.js +1 -0
- package/dist/assets/psyche-self-observation-page-BZ6FLuwa.js +1 -0
- package/dist/assets/psyche-values-page-yEV6MGt8.js +5 -0
- package/dist/assets/query-cache-IQ8W-LNC.js +1 -0
- package/dist/assets/report-chain-fields-fZ8Xd4H6.js +1 -0
- package/dist/assets/rewards-page-C2HQjIAf.js +1 -0
- package/dist/assets/scheduling-rules-editor-BHOpHOrV.js +1 -0
- package/dist/assets/schema-badge-DyKbxb51.js +1 -0
- package/dist/assets/schema-visuals-D6nxjbYC.js +1 -0
- package/dist/assets/select-menu-BX-pZNqL.js +1 -0
- package/dist/assets/settings-agents-page-VuYXTiyc.js +6 -0
- package/dist/assets/settings-bin-page-BNzvYaOk.js +1 -0
- package/dist/assets/settings-calendar-page-CjSFB53S.js +5 -0
- package/dist/assets/settings-data-page-CGSlryuI.js +1 -0
- package/dist/assets/settings-logs-page-BTK5fine.js +1 -0
- package/dist/assets/settings-mobile-page-CRaObOGo.js +1 -0
- package/dist/assets/settings-models-page-DFshpYF8.js +1 -0
- package/dist/assets/settings-page-BP81Mb5R.js +1 -0
- package/dist/assets/settings-rewards-page-CDJ1PH2G.js +1 -0
- package/dist/assets/settings-section-nav-CCFm27r2.js +1 -0
- package/dist/assets/settings-users-page-TdUocFPa.js +1 -0
- package/dist/assets/settings-wiki-page-B2zX0QQG.js +1 -0
- package/dist/assets/sleep-page-cI1GMVzk.js +1 -0
- package/dist/assets/sports-page-06LTqp0V.js +1 -0
- package/dist/assets/state-B-4sS1xO.js +1 -0
- package/dist/assets/strategies-page-DXP9Kx8s.js +1 -0
- package/dist/assets/strategy-detail-page-D6mx_Mik.js +1 -0
- package/dist/assets/strategy-dialog-BvzomTaF.js +1 -0
- package/dist/assets/{table-DewbFlTh.js → table-WfAPUppN.js} +1 -1
- package/dist/assets/task-detail-page-BIWIggdp.js +1 -0
- package/dist/assets/timebox-planning-dialog-CaCnoslG.js +1 -0
- package/dist/assets/today-page-DO2mRPT2.js +1 -0
- package/dist/assets/training-load-page-CyZ0mlEr.js +1 -0
- package/dist/assets/{ui-C2IvSrAz.js → ui-C13Nbgas.js} +4 -4
- package/dist/assets/use-psyche-focus-target-C1C_XjYG.js +1 -0
- package/dist/assets/vendor-CRS-psbw.css +1 -0
- package/dist/assets/vendor-DHkYh85p.js +1052 -0
- package/dist/assets/vitals-page-BQvEjTc6.js +1 -0
- package/dist/assets/weekly-review-page-Tp6Q9CRj.js +1 -0
- package/dist/assets/weight-loss-page-BBzlhLVV.js +1 -0
- package/dist/assets/wiki-article-markdown-DQYohmW2.js +4 -0
- package/dist/assets/wiki-editor-page-Dem_3eZv.js +26 -0
- package/dist/assets/wiki-ingest-history-page-BxoOcCoJ.js +1 -0
- package/dist/assets/wiki-ingest-modal-DhguKk3J.js +1 -0
- package/dist/assets/wiki-page-BLRxVXkl.js +1 -0
- package/dist/assets/workbench-flow-page-DqMkCCTy.js +5 -0
- package/dist/assets/workbench-page-BWd02wPw.js +1 -0
- package/dist/assets/workout-detail-page-BD8u7GyL.js +2 -0
- package/dist/index.html +148 -9
- package/dist/openclaw/tools.js +340 -0
- package/dist/server/server/migrations/065_weight_loss_nutrition_insights.sql +236 -0
- package/dist/server/server/migrations/066_watch_action_receipts.sql +20 -0
- package/dist/server/server/src/app.js +266 -13
- package/dist/server/server/src/health-weight-loss.js +1378 -0
- package/dist/server/server/src/health.js +188 -35
- package/dist/server/server/src/openapi.js +449 -0
- package/dist/server/server/src/services/context.js +6 -7
- package/dist/server/server/src/services/doctor.js +39 -4
- package/dist/server/server/src/services/gamification.js +146 -34
- package/dist/server/server/src/watch-mobile.js +564 -4
- package/dist/server/server/src/web.js +18 -5
- package/dist/server/src/components/ui/info-tooltip.js +48 -3
- package/dist/server/src/lib/api.js +131 -0
- package/dist/server/src/lib/weight-loss-types.js +1 -0
- package/openclaw.plugin.json +14 -1
- package/package.json +1 -1
- package/server/migrations/065_weight_loss_nutrition_insights.sql +236 -0
- package/server/migrations/066_watch_action_receipts.sql +20 -0
- package/skills/forge-openclaw/SKILL.md +26 -5
- package/skills/forge-openclaw/entity_conversation_playbooks.md +134 -5
- package/skills/forge-openclaw/psyche_entity_playbooks.md +45 -0
- package/dist/assets/index-Cn5Wpwau.css +0 -1
- package/dist/assets/index-CwvGs8n4.js +0 -91
- package/dist/assets/vendor-B-Lq_OG3.css +0 -1
- package/dist/assets/vendor-DL2K5ayT.js +0 -2186
package/dist/openclaw/tools.js
CHANGED
|
@@ -269,6 +269,62 @@ const healthLinkInputSchema = () => Type.Object({
|
|
|
269
269
|
entityId: Type.String({ minLength: 1 }),
|
|
270
270
|
relationshipType: Type.Optional(Type.String({ minLength: 1 }))
|
|
271
271
|
});
|
|
272
|
+
const nutritionMealItemInputSchema = () => Type.Object({
|
|
273
|
+
name: Type.String({ minLength: 1 }),
|
|
274
|
+
brand: optionalNullableString(),
|
|
275
|
+
quantity: Type.Number({ minimum: 0 }),
|
|
276
|
+
unit: optionalNullableString(),
|
|
277
|
+
caloriesKcal: Type.Optional(Type.Union([Type.Number(), Type.Null()])),
|
|
278
|
+
proteinG: Type.Optional(Type.Union([Type.Number(), Type.Null()])),
|
|
279
|
+
carbsG: Type.Optional(Type.Union([Type.Number(), Type.Null()])),
|
|
280
|
+
fatG: Type.Optional(Type.Union([Type.Number(), Type.Null()])),
|
|
281
|
+
fiberG: Type.Optional(Type.Union([Type.Number(), Type.Null()])),
|
|
282
|
+
sugarG: Type.Optional(Type.Union([Type.Number(), Type.Null()])),
|
|
283
|
+
sodiumMg: Type.Optional(Type.Union([Type.Number(), Type.Null()])),
|
|
284
|
+
potassiumMg: Type.Optional(Type.Union([Type.Number(), Type.Null()])),
|
|
285
|
+
caffeineMg: Type.Optional(Type.Union([Type.Number(), Type.Null()])),
|
|
286
|
+
alcoholG: Type.Optional(Type.Union([Type.Number(), Type.Null()])),
|
|
287
|
+
glycemicIndex: Type.Optional(Type.Union([Type.Number(), Type.Null()])),
|
|
288
|
+
novaGroup: Type.Optional(Type.Union([Type.Integer(), Type.Null()])),
|
|
289
|
+
fermented: Type.Optional(Type.Union([Type.Boolean(), Type.Null()])),
|
|
290
|
+
probiotic: Type.Optional(Type.Union([Type.Boolean(), Type.Null()])),
|
|
291
|
+
fodmapLevel: Type.Optional(Type.Union([
|
|
292
|
+
Type.Literal("low"),
|
|
293
|
+
Type.Literal("medium"),
|
|
294
|
+
Type.Literal("high"),
|
|
295
|
+
Type.Null()
|
|
296
|
+
])),
|
|
297
|
+
tags: Type.Optional(Type.Array(Type.String())),
|
|
298
|
+
confidence: Type.Optional(Type.Union([Type.Number(), Type.Null()]))
|
|
299
|
+
});
|
|
300
|
+
const nutritionUserScopeSchema = () => Type.Optional(Type.Array(Type.String({ minLength: 1 })));
|
|
301
|
+
const nutritionFoodLogSchema = () => Type.Object({
|
|
302
|
+
userIds: nutritionUserScopeSchema(),
|
|
303
|
+
loggedAt: optionalString(),
|
|
304
|
+
mealLabel: optionalNullableString(),
|
|
305
|
+
source: Type.Optional(Type.Union([
|
|
306
|
+
Type.Literal("manual"),
|
|
307
|
+
Type.Literal("barcode"),
|
|
308
|
+
Type.Literal("chatgpt"),
|
|
309
|
+
Type.Literal("photo"),
|
|
310
|
+
Type.Literal("import")
|
|
311
|
+
])),
|
|
312
|
+
confirmationState: Type.Optional(Type.Union([
|
|
313
|
+
Type.Literal("candidate"),
|
|
314
|
+
Type.Literal("confirmed"),
|
|
315
|
+
Type.Literal("corrected"),
|
|
316
|
+
Type.Literal("rejected")
|
|
317
|
+
])),
|
|
318
|
+
satietyScore: Type.Optional(Type.Union([Type.Number(), Type.Null()])),
|
|
319
|
+
hungerBefore: Type.Optional(Type.Union([Type.Number(), Type.Null()])),
|
|
320
|
+
hungerAfter: Type.Optional(Type.Union([Type.Number(), Type.Null()])),
|
|
321
|
+
cravingScore: Type.Optional(Type.Union([Type.Number(), Type.Null()])),
|
|
322
|
+
enjoymentScore: Type.Optional(Type.Union([Type.Number(), Type.Null()])),
|
|
323
|
+
socialContext: optionalNullableString(),
|
|
324
|
+
locationContext: optionalNullableString(),
|
|
325
|
+
notes: optionalNullableString(),
|
|
326
|
+
items: Type.Array(nutritionMealItemInputSchema(), { minItems: 1 })
|
|
327
|
+
});
|
|
272
328
|
const noteInputSchema = () => Type.Object({
|
|
273
329
|
contentMarkdown: Type.String({ minLength: 1 }),
|
|
274
330
|
author: optionalNullableString(),
|
|
@@ -759,6 +815,290 @@ export function registerForgePluginTools(api, config) {
|
|
|
759
815
|
parameters: scopedReadSchema,
|
|
760
816
|
path: (params) => withUserIds("/api/v1/health/training-load", params.userIds)
|
|
761
817
|
});
|
|
818
|
+
registerReadTool(api, config, {
|
|
819
|
+
name: "forge_get_weight_loss_overview",
|
|
820
|
+
label: "Forge Weight Loss Overview",
|
|
821
|
+
description: "Read the weight-loss and nutrition insight surface with calorie ledger, protein/fiber targets, energy balance, body trend, subjective energy, gut comfort, aesthetic check-ins, hypotheses, experiments, and data-quality flags.",
|
|
822
|
+
parameters: scopedReadSchema,
|
|
823
|
+
path: (params) => withUserIds("/api/v1/health/weight-loss", params.userIds)
|
|
824
|
+
});
|
|
825
|
+
api.registerTool({
|
|
826
|
+
name: "forge_search_nutrition_foods",
|
|
827
|
+
label: "Forge Search Nutrition Foods",
|
|
828
|
+
description: "Search local, Open Food Facts, and USDA-backed nutrition foods before logging a concrete food item.",
|
|
829
|
+
parameters: Type.Object({
|
|
830
|
+
userIds: nutritionUserScopeSchema(),
|
|
831
|
+
query: Type.String({ minLength: 1 }),
|
|
832
|
+
limit: Type.Optional(Type.Integer({ minimum: 1, maximum: 30 }))
|
|
833
|
+
}),
|
|
834
|
+
async execute(_toolCallId, params) {
|
|
835
|
+
const typed = params;
|
|
836
|
+
return jsonResult(await runWrite(config, {
|
|
837
|
+
method: "POST",
|
|
838
|
+
path: withUserIds("/api/v1/health/weight-loss/foods/search", typed.userIds),
|
|
839
|
+
body: { query: typed.query, limit: typed.limit }
|
|
840
|
+
}));
|
|
841
|
+
}
|
|
842
|
+
});
|
|
843
|
+
api.registerTool({
|
|
844
|
+
name: "forge_search_foods",
|
|
845
|
+
label: "Forge Search Foods",
|
|
846
|
+
description: "Search local, Open Food Facts, and USDA-backed nutrition foods before logging a concrete food item. This is the short alias for forge_search_nutrition_foods.",
|
|
847
|
+
parameters: Type.Object({
|
|
848
|
+
userIds: nutritionUserScopeSchema(),
|
|
849
|
+
query: Type.String({ minLength: 1 }),
|
|
850
|
+
limit: Type.Optional(Type.Integer({ minimum: 1, maximum: 30 }))
|
|
851
|
+
}),
|
|
852
|
+
async execute(_toolCallId, params) {
|
|
853
|
+
const typed = params;
|
|
854
|
+
return jsonResult(await runWrite(config, {
|
|
855
|
+
method: "POST",
|
|
856
|
+
path: withUserIds("/api/v1/health/weight-loss/foods/search", typed.userIds),
|
|
857
|
+
body: { query: typed.query, limit: typed.limit }
|
|
858
|
+
}));
|
|
859
|
+
}
|
|
860
|
+
});
|
|
861
|
+
api.registerTool({
|
|
862
|
+
name: "forge_lookup_nutrition_barcode",
|
|
863
|
+
label: "Forge Lookup Nutrition Barcode",
|
|
864
|
+
description: "Lookup a packaged food by barcode through Forge's nutrition catalog adapters.",
|
|
865
|
+
parameters: Type.Object({
|
|
866
|
+
userIds: nutritionUserScopeSchema(),
|
|
867
|
+
barcode: Type.String({ minLength: 1 })
|
|
868
|
+
}),
|
|
869
|
+
async execute(_toolCallId, params) {
|
|
870
|
+
const typed = params;
|
|
871
|
+
return jsonResult(await runWrite(config, {
|
|
872
|
+
method: "POST",
|
|
873
|
+
path: withUserIds("/api/v1/health/weight-loss/foods/barcode", typed.userIds),
|
|
874
|
+
body: { barcode: typed.barcode }
|
|
875
|
+
}));
|
|
876
|
+
}
|
|
877
|
+
});
|
|
878
|
+
api.registerTool({
|
|
879
|
+
name: "forge_log_food",
|
|
880
|
+
label: "Forge Log Food",
|
|
881
|
+
description: "Create a confirmed or candidate food log with explicit meal items, calories, macros, quality tags, hunger, satiety, cravings, and context.",
|
|
882
|
+
parameters: nutritionFoodLogSchema(),
|
|
883
|
+
async execute(_toolCallId, params) {
|
|
884
|
+
const typed = params;
|
|
885
|
+
const { userIds: _userIds, ...body } = typed;
|
|
886
|
+
return jsonResult(await runWrite(config, {
|
|
887
|
+
method: "POST",
|
|
888
|
+
path: withUserIds("/api/v1/health/weight-loss/food-logs", typed.userIds),
|
|
889
|
+
body
|
|
890
|
+
}));
|
|
891
|
+
}
|
|
892
|
+
});
|
|
893
|
+
api.registerTool({
|
|
894
|
+
name: "forge_parse_food_log_with_chatgpt",
|
|
895
|
+
label: "Forge Parse Food Log With ChatGPT",
|
|
896
|
+
description: "Use Forge's openai-codex ChatGPT subscription connection to parse natural-language food text or a photo description into a candidate nutrition log. This must not use the metered OpenAI API.",
|
|
897
|
+
parameters: Type.Object({
|
|
898
|
+
userIds: nutritionUserScopeSchema(),
|
|
899
|
+
text: optionalString(),
|
|
900
|
+
imageDescription: optionalString(),
|
|
901
|
+
loggedAt: optionalString(),
|
|
902
|
+
mealLabel: optionalString()
|
|
903
|
+
}),
|
|
904
|
+
async execute(_toolCallId, params) {
|
|
905
|
+
const typed = params;
|
|
906
|
+
return jsonResult(await runWrite(config, {
|
|
907
|
+
method: "POST",
|
|
908
|
+
path: withUserIds("/api/v1/health/weight-loss/parse", typed.userIds),
|
|
909
|
+
body: {
|
|
910
|
+
text: typed.text,
|
|
911
|
+
imageDescription: typed.imageDescription,
|
|
912
|
+
loggedAt: typed.loggedAt,
|
|
913
|
+
mealLabel: typed.mealLabel
|
|
914
|
+
}
|
|
915
|
+
}));
|
|
916
|
+
}
|
|
917
|
+
});
|
|
918
|
+
api.registerTool({
|
|
919
|
+
name: "forge_log_body_checkin",
|
|
920
|
+
label: "Forge Log Body Check-In",
|
|
921
|
+
description: "Record body-composition check-ins such as weight, waist, hip, neck, body-fat estimate, and notes for trend calculations.",
|
|
922
|
+
parameters: Type.Object({
|
|
923
|
+
userIds: nutritionUserScopeSchema(),
|
|
924
|
+
checkedAt: optionalString(),
|
|
925
|
+
weightKg: Type.Optional(Type.Union([Type.Number(), Type.Null()])),
|
|
926
|
+
waistCm: Type.Optional(Type.Union([Type.Number(), Type.Null()])),
|
|
927
|
+
hipCm: Type.Optional(Type.Union([Type.Number(), Type.Null()])),
|
|
928
|
+
neckCm: Type.Optional(Type.Union([Type.Number(), Type.Null()])),
|
|
929
|
+
bodyFatPercent: Type.Optional(Type.Union([Type.Number(), Type.Null()])),
|
|
930
|
+
photoAssetId: optionalNullableString(),
|
|
931
|
+
notes: optionalNullableString()
|
|
932
|
+
}),
|
|
933
|
+
async execute(_toolCallId, params) {
|
|
934
|
+
const typed = params;
|
|
935
|
+
const { userIds: _userIds, ...body } = typed;
|
|
936
|
+
return jsonResult(await runWrite(config, {
|
|
937
|
+
method: "POST",
|
|
938
|
+
path: withUserIds("/api/v1/health/weight-loss/body-checkins", typed.userIds),
|
|
939
|
+
body
|
|
940
|
+
}));
|
|
941
|
+
}
|
|
942
|
+
});
|
|
943
|
+
api.registerTool({
|
|
944
|
+
name: "forge_log_appearance_checkin",
|
|
945
|
+
label: "Forge Log Appearance Check-In",
|
|
946
|
+
description: "Record aesthetic-look metrics such as muscle fullness, leanness, vascularity, face puffiness, visual bloat, posture confidence, outfit fit, and overall aesthetic score.",
|
|
947
|
+
parameters: Type.Object({
|
|
948
|
+
userIds: nutritionUserScopeSchema(),
|
|
949
|
+
checkedAt: optionalString(),
|
|
950
|
+
muscleFullness: Type.Optional(Type.Union([Type.Number(), Type.Null()])),
|
|
951
|
+
leanness: Type.Optional(Type.Union([Type.Number(), Type.Null()])),
|
|
952
|
+
vascularity: Type.Optional(Type.Union([Type.Number(), Type.Null()])),
|
|
953
|
+
facePuffiness: Type.Optional(Type.Union([Type.Number(), Type.Null()])),
|
|
954
|
+
abdomenBloatLook: Type.Optional(Type.Union([Type.Number(), Type.Null()])),
|
|
955
|
+
postureConfidence: Type.Optional(Type.Union([Type.Number(), Type.Null()])),
|
|
956
|
+
outfitFit: Type.Optional(Type.Union([Type.Number(), Type.Null()])),
|
|
957
|
+
aestheticScore: Type.Optional(Type.Union([Type.Number(), Type.Null()])),
|
|
958
|
+
notes: optionalNullableString()
|
|
959
|
+
}),
|
|
960
|
+
async execute(_toolCallId, params) {
|
|
961
|
+
const typed = params;
|
|
962
|
+
const { userIds: _userIds, ...body } = typed;
|
|
963
|
+
return jsonResult(await runWrite(config, {
|
|
964
|
+
method: "POST",
|
|
965
|
+
path: withUserIds("/api/v1/health/weight-loss/appearance-checkins", typed.userIds),
|
|
966
|
+
body
|
|
967
|
+
}));
|
|
968
|
+
}
|
|
969
|
+
});
|
|
970
|
+
api.registerTool({
|
|
971
|
+
name: "forge_log_subjective_food_effect",
|
|
972
|
+
label: "Forge Log Subjective Food Effect",
|
|
973
|
+
description: "Record subjective food-effect metrics such as energy, mood, focus, libido, sleepiness, soreness, stress, hunger, cravings, and workout performance.",
|
|
974
|
+
parameters: Type.Object({
|
|
975
|
+
userIds: nutritionUserScopeSchema(),
|
|
976
|
+
checkedAt: optionalString(),
|
|
977
|
+
energy: Type.Optional(Type.Union([Type.Number(), Type.Null()])),
|
|
978
|
+
mood: Type.Optional(Type.Union([Type.Number(), Type.Null()])),
|
|
979
|
+
focus: Type.Optional(Type.Union([Type.Number(), Type.Null()])),
|
|
980
|
+
libido: Type.Optional(Type.Union([Type.Number(), Type.Null()])),
|
|
981
|
+
sleepiness: Type.Optional(Type.Union([Type.Number(), Type.Null()])),
|
|
982
|
+
soreness: Type.Optional(Type.Union([Type.Number(), Type.Null()])),
|
|
983
|
+
stress: Type.Optional(Type.Union([Type.Number(), Type.Null()])),
|
|
984
|
+
hunger: Type.Optional(Type.Union([Type.Number(), Type.Null()])),
|
|
985
|
+
cravings: Type.Optional(Type.Union([Type.Number(), Type.Null()])),
|
|
986
|
+
workoutPerformance: Type.Optional(Type.Union([Type.Number(), Type.Null()])),
|
|
987
|
+
timeRelation: optionalNullableString(),
|
|
988
|
+
linkedFoodLogId: optionalNullableString(),
|
|
989
|
+
notes: optionalNullableString()
|
|
990
|
+
}),
|
|
991
|
+
async execute(_toolCallId, params) {
|
|
992
|
+
const typed = params;
|
|
993
|
+
const { userIds: _userIds, ...body } = typed;
|
|
994
|
+
return jsonResult(await runWrite(config, {
|
|
995
|
+
method: "POST",
|
|
996
|
+
path: withUserIds("/api/v1/health/weight-loss/subjective-checkins", typed.userIds),
|
|
997
|
+
body
|
|
998
|
+
}));
|
|
999
|
+
}
|
|
1000
|
+
});
|
|
1001
|
+
api.registerTool({
|
|
1002
|
+
name: "forge_log_gut_checkin",
|
|
1003
|
+
label: "Forge Log Gut Check-In",
|
|
1004
|
+
description: "Record gut-health food-effect metrics such as bloating, abdominal pain, gas, reflux, nausea, stool type, stool frequency, and suspected triggers.",
|
|
1005
|
+
parameters: Type.Object({
|
|
1006
|
+
userIds: nutritionUserScopeSchema(),
|
|
1007
|
+
checkedAt: optionalString(),
|
|
1008
|
+
bloating: Type.Optional(Type.Union([Type.Number(), Type.Null()])),
|
|
1009
|
+
abdominalPain: Type.Optional(Type.Union([Type.Number(), Type.Null()])),
|
|
1010
|
+
gas: Type.Optional(Type.Union([Type.Number(), Type.Null()])),
|
|
1011
|
+
reflux: Type.Optional(Type.Union([Type.Number(), Type.Null()])),
|
|
1012
|
+
nausea: Type.Optional(Type.Union([Type.Number(), Type.Null()])),
|
|
1013
|
+
stoolType: Type.Optional(Type.Union([Type.Number(), Type.Null()])),
|
|
1014
|
+
stoolFrequency: Type.Optional(Type.Union([Type.Number(), Type.Null()])),
|
|
1015
|
+
suspectedTrigger: optionalNullableString(),
|
|
1016
|
+
linkedFoodLogId: optionalNullableString(),
|
|
1017
|
+
notes: optionalNullableString()
|
|
1018
|
+
}),
|
|
1019
|
+
async execute(_toolCallId, params) {
|
|
1020
|
+
const typed = params;
|
|
1021
|
+
const { userIds: _userIds, ...body } = typed;
|
|
1022
|
+
return jsonResult(await runWrite(config, {
|
|
1023
|
+
method: "POST",
|
|
1024
|
+
path: withUserIds("/api/v1/health/weight-loss/gut-checkins", typed.userIds),
|
|
1025
|
+
body
|
|
1026
|
+
}));
|
|
1027
|
+
}
|
|
1028
|
+
});
|
|
1029
|
+
registerReadTool(api, config, {
|
|
1030
|
+
name: "forge_get_nutrition_patterns",
|
|
1031
|
+
label: "Forge Nutrition Patterns",
|
|
1032
|
+
description: "Read current food-effect hypotheses and nutrition experiments, including links between meals, sport fueling, energy, gut comfort, cravings, and aesthetic look.",
|
|
1033
|
+
parameters: scopedReadSchema,
|
|
1034
|
+
path: (params) => withUserIds("/api/v1/health/weight-loss/patterns", params.userIds)
|
|
1035
|
+
});
|
|
1036
|
+
api.registerTool({
|
|
1037
|
+
name: "forge_start_nutrition_experiment",
|
|
1038
|
+
label: "Forge Start Nutrition Experiment",
|
|
1039
|
+
description: "Create a structured N-of-1 nutrition experiment, such as carb timing, caffeine timing, low-FODMAP trial, sodium/puffiness test, fiber ramp, or pre-training fueling.",
|
|
1040
|
+
parameters: Type.Object({
|
|
1041
|
+
userIds: nutritionUserScopeSchema(),
|
|
1042
|
+
title: Type.String({ minLength: 1 }),
|
|
1043
|
+
hypothesis: Type.String({ minLength: 1 }),
|
|
1044
|
+
metricKey: Type.String({ minLength: 1 }),
|
|
1045
|
+
intervention: Type.String({ minLength: 1 }),
|
|
1046
|
+
baselineStart: optionalNullableString(),
|
|
1047
|
+
baselineEnd: optionalNullableString(),
|
|
1048
|
+
experimentStart: optionalNullableString(),
|
|
1049
|
+
experimentEnd: optionalNullableString(),
|
|
1050
|
+
status: Type.Optional(Type.Union([
|
|
1051
|
+
Type.Literal("planned"),
|
|
1052
|
+
Type.Literal("running"),
|
|
1053
|
+
Type.Literal("completed"),
|
|
1054
|
+
Type.Literal("abandoned")
|
|
1055
|
+
])),
|
|
1056
|
+
successCriteria: optionalNullableString()
|
|
1057
|
+
}),
|
|
1058
|
+
async execute(_toolCallId, params) {
|
|
1059
|
+
const typed = params;
|
|
1060
|
+
const { userIds: _userIds, ...body } = typed;
|
|
1061
|
+
return jsonResult(await runWrite(config, {
|
|
1062
|
+
method: "POST",
|
|
1063
|
+
path: withUserIds("/api/v1/health/weight-loss/experiments", typed.userIds),
|
|
1064
|
+
body
|
|
1065
|
+
}));
|
|
1066
|
+
}
|
|
1067
|
+
});
|
|
1068
|
+
api.registerTool({
|
|
1069
|
+
name: "forge_update_nutrition_experiment",
|
|
1070
|
+
label: "Forge Update Nutrition Experiment",
|
|
1071
|
+
description: "Patch a nutrition experiment's status, dates, success criteria, intervention, hypothesis, or conclusion after new evidence arrives.",
|
|
1072
|
+
parameters: Type.Object({
|
|
1073
|
+
userIds: nutritionUserScopeSchema(),
|
|
1074
|
+
experimentId: Type.String({ minLength: 1 }),
|
|
1075
|
+
title: optionalString(),
|
|
1076
|
+
hypothesis: optionalString(),
|
|
1077
|
+
metricKey: optionalString(),
|
|
1078
|
+
intervention: optionalString(),
|
|
1079
|
+
baselineStart: optionalNullableString(),
|
|
1080
|
+
baselineEnd: optionalNullableString(),
|
|
1081
|
+
experimentStart: optionalNullableString(),
|
|
1082
|
+
experimentEnd: optionalNullableString(),
|
|
1083
|
+
status: Type.Optional(Type.Union([
|
|
1084
|
+
Type.Literal("planned"),
|
|
1085
|
+
Type.Literal("running"),
|
|
1086
|
+
Type.Literal("completed"),
|
|
1087
|
+
Type.Literal("abandoned")
|
|
1088
|
+
])),
|
|
1089
|
+
successCriteria: optionalNullableString(),
|
|
1090
|
+
conclusion: optionalNullableString()
|
|
1091
|
+
}),
|
|
1092
|
+
async execute(_toolCallId, params) {
|
|
1093
|
+
const typed = params;
|
|
1094
|
+
const { userIds: _userIds, experimentId: _experimentId, ...body } = typed;
|
|
1095
|
+
return jsonResult(await runWrite(config, {
|
|
1096
|
+
method: "PATCH",
|
|
1097
|
+
path: withUserIds(`/api/v1/health/weight-loss/experiments/${typed.experimentId}`, typed.userIds),
|
|
1098
|
+
body
|
|
1099
|
+
}));
|
|
1100
|
+
}
|
|
1101
|
+
});
|
|
762
1102
|
api.registerTool({
|
|
763
1103
|
name: "forge_update_sleep_session",
|
|
764
1104
|
label: "Forge Update Sleep Session",
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
CREATE TABLE IF NOT EXISTS nutrition_targets (
|
|
2
|
+
id TEXT PRIMARY KEY,
|
|
3
|
+
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
4
|
+
calorie_target REAL,
|
|
5
|
+
protein_grams_target REAL,
|
|
6
|
+
fiber_grams_target REAL,
|
|
7
|
+
carbohydrate_grams_target REAL,
|
|
8
|
+
fat_grams_target REAL,
|
|
9
|
+
weight_goal_kg REAL,
|
|
10
|
+
weekly_rate_goal_kg REAL,
|
|
11
|
+
diet_style TEXT NOT NULL DEFAULT '',
|
|
12
|
+
body_goal TEXT NOT NULL DEFAULT '',
|
|
13
|
+
notes TEXT NOT NULL DEFAULT '',
|
|
14
|
+
created_at TEXT NOT NULL,
|
|
15
|
+
updated_at TEXT NOT NULL
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_nutrition_targets_user
|
|
19
|
+
ON nutrition_targets(user_id);
|
|
20
|
+
|
|
21
|
+
CREATE TABLE IF NOT EXISTS nutrition_food_catalog (
|
|
22
|
+
id TEXT PRIMARY KEY,
|
|
23
|
+
source TEXT NOT NULL,
|
|
24
|
+
source_id TEXT NOT NULL,
|
|
25
|
+
barcode TEXT,
|
|
26
|
+
name TEXT NOT NULL,
|
|
27
|
+
brand TEXT NOT NULL DEFAULT '',
|
|
28
|
+
serving_label TEXT NOT NULL DEFAULT '',
|
|
29
|
+
serving_grams REAL,
|
|
30
|
+
calories REAL,
|
|
31
|
+
protein_grams REAL,
|
|
32
|
+
carbohydrate_grams REAL,
|
|
33
|
+
fat_grams REAL,
|
|
34
|
+
fiber_grams REAL,
|
|
35
|
+
sugar_grams REAL,
|
|
36
|
+
sodium_mg REAL,
|
|
37
|
+
potassium_mg REAL,
|
|
38
|
+
caffeine_mg REAL,
|
|
39
|
+
alcohol_grams REAL,
|
|
40
|
+
nova_group INTEGER,
|
|
41
|
+
nutri_score TEXT,
|
|
42
|
+
tags_json TEXT NOT NULL DEFAULT '[]',
|
|
43
|
+
nutrients_json TEXT NOT NULL DEFAULT '{}',
|
|
44
|
+
confidence REAL NOT NULL DEFAULT 0.65,
|
|
45
|
+
created_at TEXT NOT NULL,
|
|
46
|
+
updated_at TEXT NOT NULL
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_nutrition_food_catalog_source
|
|
50
|
+
ON nutrition_food_catalog(source, source_id);
|
|
51
|
+
|
|
52
|
+
CREATE INDEX IF NOT EXISTS idx_nutrition_food_catalog_barcode
|
|
53
|
+
ON nutrition_food_catalog(barcode);
|
|
54
|
+
|
|
55
|
+
CREATE TABLE IF NOT EXISTS nutrition_food_logs (
|
|
56
|
+
id TEXT PRIMARY KEY,
|
|
57
|
+
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
58
|
+
logged_at TEXT NOT NULL,
|
|
59
|
+
meal_label TEXT NOT NULL DEFAULT '',
|
|
60
|
+
source TEXT NOT NULL DEFAULT 'manual',
|
|
61
|
+
confirmation_state TEXT NOT NULL DEFAULT 'confirmed',
|
|
62
|
+
notes TEXT NOT NULL DEFAULT '',
|
|
63
|
+
place_id TEXT,
|
|
64
|
+
stay_id TEXT,
|
|
65
|
+
workout_id TEXT,
|
|
66
|
+
sleep_id TEXT,
|
|
67
|
+
day_key TEXT NOT NULL,
|
|
68
|
+
image_refs_json TEXT NOT NULL DEFAULT '[]',
|
|
69
|
+
parser_provenance_json TEXT NOT NULL DEFAULT '{}',
|
|
70
|
+
links_json TEXT NOT NULL DEFAULT '[]',
|
|
71
|
+
created_at TEXT NOT NULL,
|
|
72
|
+
updated_at TEXT NOT NULL
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
CREATE INDEX IF NOT EXISTS idx_nutrition_food_logs_user_time
|
|
76
|
+
ON nutrition_food_logs(user_id, logged_at DESC);
|
|
77
|
+
|
|
78
|
+
CREATE INDEX IF NOT EXISTS idx_nutrition_food_logs_day
|
|
79
|
+
ON nutrition_food_logs(user_id, day_key);
|
|
80
|
+
|
|
81
|
+
CREATE TABLE IF NOT EXISTS nutrition_meal_items (
|
|
82
|
+
id TEXT PRIMARY KEY,
|
|
83
|
+
log_id TEXT NOT NULL REFERENCES nutrition_food_logs(id) ON DELETE CASCADE,
|
|
84
|
+
food_id TEXT REFERENCES nutrition_food_catalog(id) ON DELETE SET NULL,
|
|
85
|
+
name TEXT NOT NULL,
|
|
86
|
+
quantity REAL NOT NULL DEFAULT 1,
|
|
87
|
+
unit TEXT NOT NULL DEFAULT 'serving',
|
|
88
|
+
grams REAL,
|
|
89
|
+
calories REAL,
|
|
90
|
+
protein_grams REAL,
|
|
91
|
+
carbohydrate_grams REAL,
|
|
92
|
+
fat_grams REAL,
|
|
93
|
+
fiber_grams REAL,
|
|
94
|
+
sugar_grams REAL,
|
|
95
|
+
sodium_mg REAL,
|
|
96
|
+
potassium_mg REAL,
|
|
97
|
+
caffeine_mg REAL,
|
|
98
|
+
alcohol_grams REAL,
|
|
99
|
+
tags_json TEXT NOT NULL DEFAULT '[]',
|
|
100
|
+
nutrients_json TEXT NOT NULL DEFAULT '{}',
|
|
101
|
+
confidence REAL NOT NULL DEFAULT 0.65,
|
|
102
|
+
created_at TEXT NOT NULL,
|
|
103
|
+
updated_at TEXT NOT NULL
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
CREATE INDEX IF NOT EXISTS idx_nutrition_meal_items_log
|
|
107
|
+
ON nutrition_meal_items(log_id);
|
|
108
|
+
|
|
109
|
+
CREATE TABLE IF NOT EXISTS nutrition_body_checkins (
|
|
110
|
+
id TEXT PRIMARY KEY,
|
|
111
|
+
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
112
|
+
checked_at TEXT NOT NULL,
|
|
113
|
+
weight_kg REAL,
|
|
114
|
+
waist_cm REAL,
|
|
115
|
+
hip_cm REAL,
|
|
116
|
+
neck_cm REAL,
|
|
117
|
+
chest_cm REAL,
|
|
118
|
+
arm_cm REAL,
|
|
119
|
+
thigh_cm REAL,
|
|
120
|
+
body_fat_percent REAL,
|
|
121
|
+
clothing_fit_score INTEGER,
|
|
122
|
+
notes TEXT NOT NULL DEFAULT '',
|
|
123
|
+
created_at TEXT NOT NULL,
|
|
124
|
+
updated_at TEXT NOT NULL
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
CREATE INDEX IF NOT EXISTS idx_nutrition_body_checkins_user_time
|
|
128
|
+
ON nutrition_body_checkins(user_id, checked_at DESC);
|
|
129
|
+
|
|
130
|
+
CREATE TABLE IF NOT EXISTS nutrition_appearance_checkins (
|
|
131
|
+
id TEXT PRIMARY KEY,
|
|
132
|
+
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
133
|
+
checked_at TEXT NOT NULL,
|
|
134
|
+
photo_refs_json TEXT NOT NULL DEFAULT '[]',
|
|
135
|
+
face_puffiness INTEGER,
|
|
136
|
+
leanness INTEGER,
|
|
137
|
+
muscularity INTEGER,
|
|
138
|
+
posture INTEGER,
|
|
139
|
+
bloating_look INTEGER,
|
|
140
|
+
confidence_score INTEGER,
|
|
141
|
+
notes TEXT NOT NULL DEFAULT '',
|
|
142
|
+
created_at TEXT NOT NULL,
|
|
143
|
+
updated_at TEXT NOT NULL
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
CREATE INDEX IF NOT EXISTS idx_nutrition_appearance_checkins_user_time
|
|
147
|
+
ON nutrition_appearance_checkins(user_id, checked_at DESC);
|
|
148
|
+
|
|
149
|
+
CREATE TABLE IF NOT EXISTS nutrition_subjective_checkins (
|
|
150
|
+
id TEXT PRIMARY KEY,
|
|
151
|
+
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
152
|
+
checked_at TEXT NOT NULL,
|
|
153
|
+
meal_log_id TEXT REFERENCES nutrition_food_logs(id) ON DELETE SET NULL,
|
|
154
|
+
time_relation TEXT NOT NULL DEFAULT 'unspecified',
|
|
155
|
+
hunger INTEGER,
|
|
156
|
+
fullness INTEGER,
|
|
157
|
+
cravings INTEGER,
|
|
158
|
+
mood INTEGER,
|
|
159
|
+
energy INTEGER,
|
|
160
|
+
focus INTEGER,
|
|
161
|
+
stress INTEGER,
|
|
162
|
+
sleepiness INTEGER,
|
|
163
|
+
crash_score INTEGER,
|
|
164
|
+
notes TEXT NOT NULL DEFAULT '',
|
|
165
|
+
created_at TEXT NOT NULL,
|
|
166
|
+
updated_at TEXT NOT NULL
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
CREATE INDEX IF NOT EXISTS idx_nutrition_subjective_checkins_user_time
|
|
170
|
+
ON nutrition_subjective_checkins(user_id, checked_at DESC);
|
|
171
|
+
|
|
172
|
+
CREATE TABLE IF NOT EXISTS nutrition_gut_checkins (
|
|
173
|
+
id TEXT PRIMARY KEY,
|
|
174
|
+
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
175
|
+
checked_at TEXT NOT NULL,
|
|
176
|
+
meal_log_id TEXT REFERENCES nutrition_food_logs(id) ON DELETE SET NULL,
|
|
177
|
+
bristol_stool_type INTEGER,
|
|
178
|
+
stool_frequency INTEGER,
|
|
179
|
+
bloating INTEGER,
|
|
180
|
+
gas INTEGER,
|
|
181
|
+
reflux INTEGER,
|
|
182
|
+
abdominal_pain INTEGER,
|
|
183
|
+
urgency INTEGER,
|
|
184
|
+
nausea INTEGER,
|
|
185
|
+
constipation INTEGER,
|
|
186
|
+
diarrhea INTEGER,
|
|
187
|
+
trigger_tags_json TEXT NOT NULL DEFAULT '[]',
|
|
188
|
+
notes TEXT NOT NULL DEFAULT '',
|
|
189
|
+
created_at TEXT NOT NULL,
|
|
190
|
+
updated_at TEXT NOT NULL
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
CREATE INDEX IF NOT EXISTS idx_nutrition_gut_checkins_user_time
|
|
194
|
+
ON nutrition_gut_checkins(user_id, checked_at DESC);
|
|
195
|
+
|
|
196
|
+
CREATE TABLE IF NOT EXISTS nutrition_hypotheses (
|
|
197
|
+
id TEXT PRIMARY KEY,
|
|
198
|
+
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
199
|
+
title TEXT NOT NULL,
|
|
200
|
+
summary TEXT NOT NULL DEFAULT '',
|
|
201
|
+
status TEXT NOT NULL DEFAULT 'candidate',
|
|
202
|
+
confidence REAL NOT NULL DEFAULT 0.25,
|
|
203
|
+
evidence_count INTEGER NOT NULL DEFAULT 0,
|
|
204
|
+
signal_key TEXT NOT NULL DEFAULT '',
|
|
205
|
+
outcome_key TEXT NOT NULL DEFAULT '',
|
|
206
|
+
lag_window TEXT NOT NULL DEFAULT '',
|
|
207
|
+
evidence_json TEXT NOT NULL DEFAULT '{}',
|
|
208
|
+
confounders_json TEXT NOT NULL DEFAULT '[]',
|
|
209
|
+
suggested_action TEXT NOT NULL DEFAULT '',
|
|
210
|
+
created_at TEXT NOT NULL,
|
|
211
|
+
updated_at TEXT NOT NULL
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
CREATE INDEX IF NOT EXISTS idx_nutrition_hypotheses_user_status
|
|
215
|
+
ON nutrition_hypotheses(user_id, status, updated_at DESC);
|
|
216
|
+
|
|
217
|
+
CREATE TABLE IF NOT EXISTS nutrition_experiments (
|
|
218
|
+
id TEXT PRIMARY KEY,
|
|
219
|
+
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
220
|
+
hypothesis_id TEXT REFERENCES nutrition_hypotheses(id) ON DELETE SET NULL,
|
|
221
|
+
title TEXT NOT NULL,
|
|
222
|
+
status TEXT NOT NULL DEFAULT 'planned',
|
|
223
|
+
baseline_start TEXT,
|
|
224
|
+
baseline_end TEXT,
|
|
225
|
+
intervention_start TEXT,
|
|
226
|
+
intervention_end TEXT,
|
|
227
|
+
tracked_outcomes_json TEXT NOT NULL DEFAULT '[]',
|
|
228
|
+
protocol_json TEXT NOT NULL DEFAULT '{}',
|
|
229
|
+
adherence_json TEXT NOT NULL DEFAULT '{}',
|
|
230
|
+
result_summary TEXT NOT NULL DEFAULT '',
|
|
231
|
+
created_at TEXT NOT NULL,
|
|
232
|
+
updated_at TEXT NOT NULL
|
|
233
|
+
);
|
|
234
|
+
|
|
235
|
+
CREATE INDEX IF NOT EXISTS idx_nutrition_experiments_user_status
|
|
236
|
+
ON nutrition_experiments(user_id, status, updated_at DESC);
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
CREATE TABLE IF NOT EXISTS watch_action_receipts (
|
|
2
|
+
id TEXT PRIMARY KEY,
|
|
3
|
+
pairing_session_id TEXT REFERENCES companion_pairing_sessions(id) ON DELETE SET NULL,
|
|
4
|
+
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
5
|
+
action_id TEXT NOT NULL,
|
|
6
|
+
kind TEXT NOT NULL,
|
|
7
|
+
received_at TEXT NOT NULL,
|
|
8
|
+
processed_at TEXT NOT NULL,
|
|
9
|
+
status TEXT NOT NULL,
|
|
10
|
+
result_json TEXT NOT NULL DEFAULT '{}',
|
|
11
|
+
error_json TEXT NOT NULL DEFAULT '{}',
|
|
12
|
+
created_at TEXT NOT NULL,
|
|
13
|
+
UNIQUE (user_id, action_id)
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
CREATE INDEX IF NOT EXISTS idx_watch_action_receipts_user_processed
|
|
17
|
+
ON watch_action_receipts(user_id, processed_at DESC);
|
|
18
|
+
|
|
19
|
+
CREATE INDEX IF NOT EXISTS idx_watch_action_receipts_kind
|
|
20
|
+
ON watch_action_receipts(user_id, kind, processed_at DESC);
|