forge-openclaw-plugin 0.3.4 → 0.3.6
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/{action-bar-CMGr_Uv_.js → action-bar-CsDQF9d4.js} +1 -1
- package/dist/assets/{activity-page-DwGKkJtr.js → activity-page-CACf1Sd5.js} +1 -1
- package/dist/assets/{ai-surface-workspace-UtnoNmjq.js → ai-surface-workspace-Vn5fqCAT.js} +1 -1
- package/dist/assets/{atlas-panel-f1kc-LkD.js → atlas-panel-GdK1oyxo.js} +1 -1
- package/dist/assets/{board-CLOHbg6t.js → board-CuxQRKPJ.js} +1 -1
- package/dist/assets/{calendar-page-C4YqeVrC.js → calendar-page-CcVGbvfY.js} +1 -1
- package/dist/assets/{calendar-rules-BQppCPiN.js → calendar-rules-Dlq0KT93.js} +1 -1
- package/dist/assets/{calendar-week-toolbar-DdPuHUwd.js → calendar-week-toolbar-BVFb1_2G.js} +1 -1
- package/dist/assets/{charts-DwTguE_x.js → charts-BzT4pUPg.js} +1 -1
- package/dist/assets/{companion-sync-lab-page-N3cR33GS.js → companion-sync-lab-page-Dgvtdc4i.js} +1 -1
- package/dist/assets/{daily-metrics-dashboard-CXnAtN2T.js → daily-metrics-dashboard-ChQVbNJI.js} +1 -1
- package/dist/assets/date-keys-BnZV4PNO.js +1 -0
- package/dist/assets/{define-workbench-box-DkffygDU.js → define-workbench-box-DWAqqjvH.js} +1 -1
- package/dist/assets/{entity-link-multiselect-wuyJ8BbW.js → entity-link-multiselect-Do5DTju7.js} +1 -1
- package/dist/assets/{entity-note-count-link-CEtKyMAl.js → entity-note-count-link-CV3eOv66.js} +1 -1
- package/dist/assets/{entity-notes-surface-iKNiierJ.js → entity-notes-surface-CiUgAVl7.js} +1 -1
- package/dist/assets/{execution-board-DesFZ1f5.js → execution-board-ClaXVQ0H.js} +1 -1
- package/dist/assets/{faceted-token-search-D4Okr-Kr.js → faceted-token-search-DoSsb7qT.js} +1 -1
- package/dist/assets/{flagship-signal-deck-CW91n6u9.js → flagship-signal-deck-B2s7TrIN.js} +1 -1
- package/dist/assets/{floating-action-menu-GCeuOdYZ.js → floating-action-menu-DGkHCmvI.js} +1 -1
- package/dist/assets/{forms-C5d5hTf2.js → forms-D1qJ3oOP.js} +1 -1
- package/dist/assets/{generic-node-view-COhU_Wm3.js → generic-node-view-CgLkbts-.js} +1 -1
- package/dist/assets/{goal-detail-page-Bd8JU3IK.js → goal-detail-page-JRcEYpeA.js} +1 -1
- package/dist/assets/{goal-dialog-CLz5fYhO.js → goal-dialog-6R2uYWPQ.js} +1 -1
- package/dist/assets/{goals-page-CZHNVyY-.js → goals-page-CYestXkp.js} +1 -1
- package/dist/assets/{graph-Cd5WF3lw.js → graph-BF4IsheG.js} +1 -1
- package/dist/assets/{habits-page-CXiBB7_N.js → habits-page-B27lnyKu.js} +1 -1
- package/dist/assets/{health-boxes-BUcsxDLh.js → health-boxes-CeqSGyZ3.js} +1 -1
- package/dist/assets/index-DGYIFHgo.js +2 -0
- package/dist/assets/{index-CEgIwgk9.css → index-UzsVTD1W.css} +1 -1
- package/dist/assets/{inline-note-fields-CcRDO3pd.js → inline-note-fields-CJ9ukqFp.js} +1 -1
- package/dist/assets/{insight-flow-dialog-DUBk2MR8.js → insight-flow-dialog-BgaJRYGX.js} +1 -1
- package/dist/assets/{insights-page-8fLhCYsM.js → insights-page-Dc5bilGE.js} +1 -1
- package/dist/assets/{kanban-boxes-CH7plRk1.js → kanban-boxes-qIwmQlRq.js} +1 -1
- package/dist/assets/{kanban-page-Bq-aYwUL.js → kanban-page-BGnVLHeN.js} +1 -1
- package/dist/assets/{knowledge-graph-page-DYFfWh4Y.js → knowledge-graph-page-B7IsVVO_.js} +1 -1
- package/dist/assets/{life-force-page-D80rgBBR.js → life-force-page-B5N4DknK.js} +1 -1
- package/dist/assets/{life-force-workspace-CcmZRRfU.js → life-force-workspace-Da-dmoRX.js} +1 -1
- package/dist/assets/{maps-D0Mm6WPG.js → maps-BTVHALP8.js} +1 -1
- package/dist/assets/{metric-tile-DysHMgM3.js → metric-tile-BVuj7mc3.js} +1 -1
- package/dist/assets/{motion-DwjmC9aq.js → motion-DcgUnXhY.js} +1 -1
- package/dist/assets/{movement-boxes-BH3botKt.js → movement-boxes-BZrdIh8b.js} +1 -1
- package/dist/assets/{movement-page-D5KLMKS9.js → movement-page-CY2XLgp_.js} +1 -1
- package/dist/assets/{note-markdown-Byd_vNkS.js → note-markdown-BZHBOkEd.js} +1 -1
- package/dist/assets/{note-tags-input-DYAFDq8Q.js → note-tags-input-DucocvNH.js} +1 -1
- package/dist/assets/{notes-boxes-CPiPps1e.js → notes-boxes-DAQ6KBkJ.js} +1 -1
- package/dist/assets/{notes-page-DehVrOOb.js → notes-page-CSCfdDJl.js} +1 -1
- package/dist/assets/{open-in-graph-button-L21MKsb5.js → open-in-graph-button-ObO3m8yd.js} +1 -1
- package/dist/assets/{orbit-map-ChJOQ8CN.js → orbit-map-Bteg-ola.js} +1 -1
- package/dist/assets/overview-page-ckfN_sLZ.js +1 -0
- package/dist/assets/{page-hero-DlHEJ0Yt.js → page-hero-BkrRTu-t.js} +1 -1
- package/dist/assets/pill-cluster-COzv8VgR.js +1 -0
- package/dist/assets/{preference-entity-handoff-button-ugHU2us9.js → preference-entity-handoff-button-BpVAeFmY.js} +1 -1
- package/dist/assets/{preferences-page-BTTXeSDR.js → preferences-page-DseOh9AP.js} +1 -1
- package/dist/assets/{project-collections-USb6PUe0.js → project-collections-C7yU5PSi.js} +1 -1
- package/dist/assets/{project-detail-page-1tD6tUsB.js → project-detail-page-Crt46y_7.js} +1 -1
- package/dist/assets/{project-dialog-BaHhJqP6.js → project-dialog-YYxtlqg8.js} +1 -1
- package/dist/assets/{project-management-hierarchy-page-D4HV8QXs.js → project-management-hierarchy-page-DnojKHzy.js} +1 -1
- package/dist/assets/{project-management-section-nav-9OV6_ifH.js → project-management-section-nav-Ctjd348W.js} +1 -1
- package/dist/assets/{projects-boxes-AyudWls3.js → projects-boxes-D8lLFGBC.js} +1 -1
- package/dist/assets/{projects-page-DizVVzbt.js → projects-page-CV_2em8d.js} +1 -1
- package/dist/assets/{psyche-behaviors-page-CX5MJjWj.js → psyche-behaviors-page-CYCNk0rz.js} +1 -1
- package/dist/assets/{psyche-flashcards-page-DzCGy-FV.js → psyche-flashcards-page-DwmjBixN.js} +1 -1
- package/dist/assets/{psyche-goal-map-page-BcKR792E.js → psyche-goal-map-page-CdMhyBKh.js} +1 -1
- package/dist/assets/{psyche-graph-BNllZR67.js → psyche-graph-BcrZcJrq.js} +1 -1
- package/dist/assets/{psyche-metrics-page-CpEKlZLO.js → psyche-metrics-page-D5bX1mNX.js} +1 -1
- package/dist/assets/{psyche-mode-guide-page-DXKSE2o7.js → psyche-mode-guide-page-BmX9EOOB.js} +1 -1
- package/dist/assets/{psyche-modes-page-Bh6eeNm1.js → psyche-modes-page-CTHFDyLE.js} +1 -1
- package/dist/assets/{psyche-page-B44RJrrC.js → psyche-page-DRR3Fjv-.js} +1 -1
- package/dist/assets/{psyche-patterns-page-Bm7T5FMJ.js → psyche-patterns-page-BAS38xYf.js} +1 -1
- package/dist/assets/{psyche-questionnaire-builder-page-BNtMNjY8.js → psyche-questionnaire-builder-page-DpSi-zg3.js} +1 -1
- package/dist/assets/{psyche-questionnaire-detail-page-BnMXErRK.js → psyche-questionnaire-detail-page-BcXIiV4E.js} +1 -1
- package/dist/assets/{psyche-questionnaire-run-detail-page-CWKxbdwZ.js → psyche-questionnaire-run-detail-page-DvccwzWO.js} +1 -1
- package/dist/assets/{psyche-questionnaire-run-page-DwH7Sk52.js → psyche-questionnaire-run-page-vcyon8IZ.js} +1 -1
- package/dist/assets/{psyche-questionnaires-page-B1t_a8QU.js → psyche-questionnaires-page-B0DDTner.js} +1 -1
- package/dist/assets/{psyche-report-detail-page-x2AtC99e.js → psyche-report-detail-page-BgBA3xIO.js} +1 -1
- package/dist/assets/{psyche-reports-page-KiwCgWPa.js → psyche-reports-page-Ce1vEEqW.js} +1 -1
- package/dist/assets/{psyche-schemas-Dtskzvv1.js → psyche-schemas-DDol0j-g.js} +1 -1
- package/dist/assets/{psyche-schemas-beliefs-page-CIEoAJAp.js → psyche-schemas-beliefs-page-D-IHHohY.js} +1 -1
- package/dist/assets/{psyche-screen-time-page-DO6UycL9.js → psyche-screen-time-page-DOKW3m8B.js} +1 -1
- package/dist/assets/{psyche-self-observation-page-CPcpUSdm.js → psyche-self-observation-page-j7Sk6Ns1.js} +1 -1
- package/dist/assets/{psyche-values-page-BAgNeo4L.js → psyche-values-page-NfzdWUyb.js} +1 -1
- package/dist/assets/{question-flow-dialog-BByFV8Yw.js → question-flow-dialog-CLHVmFON.js} +1 -1
- package/dist/assets/{report-chain-fields-BpRWMp7h.js → report-chain-fields-C9MuyBha.js} +1 -1
- package/dist/assets/{rewards-page-kIiX8SHn.js → rewards-page-CVxOBP6m.js} +1 -1
- package/dist/assets/{scheduling-rules-editor-oQXGcL5l.js → scheduling-rules-editor-BmbOHH_R.js} +1 -1
- package/dist/assets/{schema-badge-BoNjw3h4.js → schema-badge-BAiS_OAp.js} +1 -1
- package/dist/assets/{schemas-IBbtdPzn.js → schemas-B0AXfuOr.js} +1 -1
- package/dist/assets/{select-menu-U-YryCwP.js → select-menu-Bl5MILOj.js} +1 -1
- package/dist/assets/{settings-agents-page-CbHeMq3e.js → settings-agents-page-BIRP02mt.js} +1 -1
- package/dist/assets/{settings-bin-page-LxS2YPsA.js → settings-bin-page-DxJKeM28.js} +1 -1
- package/dist/assets/{settings-calendar-page-CZc-I-HK.js → settings-calendar-page-aMBtwvO_.js} +1 -1
- package/dist/assets/{settings-data-page-m-aBRMR9.js → settings-data-page-nHNergsh.js} +1 -1
- package/dist/assets/{settings-logs-page-BUEGWlQu.js → settings-logs-page-CFotPoFy.js} +1 -1
- package/dist/assets/{settings-mobile-page-C_5CHjHl.js → settings-mobile-page-Bf_1D-bN.js} +1 -1
- package/dist/assets/{settings-models-page-B4HQu8kL.js → settings-models-page-BbFNmnik.js} +1 -1
- package/dist/assets/{settings-page-CY8N-fNV.js → settings-page-CalbNbcg.js} +1 -1
- package/dist/assets/{settings-rewards-page-C0XQoWjb.js → settings-rewards-page-ByDfCzP5.js} +1 -1
- package/dist/assets/{settings-section-nav-CkqqtM0F.js → settings-section-nav-x_JXfzL9.js} +1 -1
- package/dist/assets/{settings-users-page-COCBOq7d.js → settings-users-page-N1jl9hWV.js} +1 -1
- package/dist/assets/{settings-wiki-page-Dy1d7-z-.js → settings-wiki-page-Bn2Qr94L.js} +1 -1
- package/dist/assets/{sleep-page-1kKicdFq.js → sleep-page-DyU635jb.js} +1 -1
- package/dist/assets/{sports-page-DJErRii_.js → sports-page-DbK4yGrR.js} +1 -1
- package/dist/assets/{state-BtwEvpO6.js → state-Bpe5dF3T.js} +1 -1
- package/dist/assets/{strategies-page-CWNFb9vv.js → strategies-page-AUBlFCZ9.js} +1 -1
- package/dist/assets/{strategy-detail-page-D9rFVXRS.js → strategy-detail-page-DCF8mVaL.js} +1 -1
- package/dist/assets/{strategy-dialog-BIVHfUR6.js → strategy-dialog-50xKlqcz.js} +1 -1
- package/dist/assets/{surface-B3oXO1No.js → surface-BjT1dIAF.js} +1 -1
- package/dist/assets/{table-BuONJH1s.js → table-U7otr5go.js} +1 -1
- package/dist/assets/{task-detail-page-r8ipOpAF.js → task-detail-page-Dsll2LCX.js} +1 -1
- package/dist/assets/{task-dialog-C3kkWtcT.js → task-dialog-BfPkbIPE.js} +1 -1
- package/dist/assets/{timebox-planning-dialog-BadS5tEr.js → timebox-planning-dialog-BTKUS6Jj.js} +1 -1
- package/dist/assets/{today-boxes-CiccN0Gw.js → today-boxes-BPi9bDHM.js} +1 -1
- package/dist/assets/{today-page-DvQredbl.js → today-page-BSXSA-Ts.js} +1 -1
- package/dist/assets/{training-load-page-CkX-nSoe.js → training-load-page-BD0s8-Zm.js} +1 -1
- package/dist/assets/{ui-B9O-eUim.js → ui-B9TWEtCx.js} +1 -1
- package/dist/assets/{use-anchored-overlay-position-BrQ4cqKn.js → use-anchored-overlay-position-BY4kNzPj.js} +1 -1
- package/dist/assets/{use-psyche-focus-target-Cuxni3SK.js → use-psyche-focus-target-BhNedCZB.js} +1 -1
- package/dist/assets/{user-badge-BKzwGZDd.js → user-badge-ap1PvOxd.js} +1 -1
- package/dist/assets/{user-select-field-BNqv8-wd.js → user-select-field-fx129Uh6.js} +1 -1
- package/dist/assets/{utility-widgets-DvxKA6dI.js → utility-widgets-Cq508sqJ.js} +1 -1
- package/dist/assets/{vendor-Cpmju3nw.js → vendor-BwL6m4SE.js} +216 -211
- package/dist/assets/{vitals-page-CRIsr_C7.js → vitals-page-BnXk8OzN.js} +1 -1
- package/dist/assets/{weekly-review-page-CNqxLRCd.js → weekly-review-page-CnmSGnEC.js} +1 -1
- package/dist/assets/weight-loss-page-Bn6lAXNf.js +5 -0
- package/dist/assets/{wiki-article-markdown-C9VKineE.js → wiki-article-markdown-BDnkdNDC.js} +1 -1
- package/dist/assets/{wiki-editor-page-DxGsKvF7.js → wiki-editor-page-CHEETB0G.js} +1 -1
- package/dist/assets/{wiki-ingest-history-page-CFmP7K1F.js → wiki-ingest-history-page-CisvtAzm.js} +1 -1
- package/dist/assets/{wiki-ingest-modal-BLw2PSWP.js → wiki-ingest-modal-C2faIjzj.js} +1 -1
- package/dist/assets/{wiki-page-BiQ-jAuz.js → wiki-page-CELe_6qL.js} +1 -1
- package/dist/assets/{workbench-flow-page-D960hXu_.js → workbench-flow-page-JBVim3L0.js} +1 -1
- package/dist/assets/{workbench-page-BWlY-FiL.js → workbench-page-q7Z3sQtz.js} +1 -1
- package/dist/assets/{workout-detail-page-G32yJoE9.js → workout-detail-page-9I_3DPYU.js} +2 -2
- package/dist/index.html +8 -8
- package/dist/server/server/migrations/070_health_mobile_sync_completion_index.sql +16 -0
- package/dist/server/server/src/app.js +139 -14
- package/dist/server/server/src/health-weight-loss.js +48 -6
- package/dist/server/server/src/health.js +348 -112
- package/dist/server/server/src/openapi.js +31 -0
- package/dist/server/server/src/repositories/gamification.js +25 -0
- package/dist/server/server/src/repositories/tasks.js +82 -17
- package/dist/server/server/src/services/dashboard.js +5 -4
- package/dist/server/server/src/services/gamification.js +47 -19
- package/dist/server/server/src/services/life-force.js +99 -0
- package/dist/server/src/lib/api.js +8 -1
- package/dist/server/src/lib/snapshot-normalizer.js +42 -23
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/server/migrations/070_health_mobile_sync_completion_index.sql +16 -0
- package/skills/forge-openclaw/SKILL.md +1 -0
- package/skills/forge-openclaw/psyche_entity_playbooks.md +8 -0
- package/dist/assets/date-keys-Cj1G3TOn.js +0 -1
- package/dist/assets/index-BejvcRGb.js +0 -2
- package/dist/assets/overview-page-D_ewyeWF.js +0 -1
- package/dist/assets/pill-cluster-DhOR4m7X.js +0 -1
- package/dist/assets/weight-loss-page-SSsCp1Yw.js +0 -5
|
@@ -5459,7 +5459,7 @@ function buildAgentOnboardingPayload(request) {
|
|
|
5459
5459
|
reviewShortcutRule: "When the user is reviewing or correcting an existing record, ask what practical question they want the read or correction to answer, then narrow the saved object, timeframe, or route family first. Use the correct read posture before asking write-shaped questions: shared batch search or read hints for normal entities, wiki/calendar dedicated reads for specialized CRUD, read-model routes for overviews, and Movement, Life Force, or Workbench dedicated reads for those domain surfaces. After the read, answer the practical question before asking for any save, correction, link, run, enrichment, or publish detail. Do not reopen the whole intake unless the user is actually redefining the record.",
|
|
5460
5460
|
readModelWriteRule: "Self-observation is note-backed and should be written through observed notes with frontmatter.observedAt only when a lightweight episode observation is the right container. Do not use it as the default bucket for Psyche material: prefer trigger_report for one emotionally meaningful episode, behavior_pattern for functional analysis of a recurring loop, behavior for one repeated move, belief_entry for a core sentence, mode_guide_session or mode_profile for a central part-state, and wiki_page for durable memory such as books, articles, concepts, sources, or personal manuals. Sleep and workout sessions stay on batch CRUD by default; use the reflective review helpers only when enriching one already-known record after review.",
|
|
5461
5461
|
psycheOpeningQuestionRule: "Prefer a concrete opening question tied to the entity: ask when the value mattered, what happened the last time the pattern appeared, what cue or body signal came first before the behavior, what the belief starts saying about self or outcome, what feels most at risk inside the mode, what the part is trying to get the user to do or stop doing, or where the shift began in the incident. Reflect briefly before the question, choose one follow-up lane at a time, say what is becoming clearer before the next deeper question, and if several Psyche entities are visible hold the adjacent ones lightly until the main container is clear.",
|
|
5462
|
-
psycheHypothesisRule: "When one concrete Psyche example is visible, a helpful hypothesis should start from evidence in the user's own example, offer one testable interpretation, name the function without blame such as protection, prediction, relief, or cost, and ask whether the danger, need, or wording fits. Use the hypothesis timing checkpoint before asking a second or third deepening question: offer a hypothesis when one concrete episode, body cue, belief sentence, behavior, or mode voice is visible and the hypothesis would change the record shape, wording, links, or next action. Do not hypothesize yet when no concrete moment is visible, the user only wants a direct mechanical save, the user is flooded or unsafe, or the only available interpretation would be diagnosis-like, an origin story, or a certainty claim. Do not present schema, mode, belief, or pattern language as a verdict. If the user corrects the hypothesis, revise it once and move toward the saveable record shape instead of asking for another broad story.",
|
|
5462
|
+
psycheHypothesisRule: "When one concrete Psyche example is visible, a helpful hypothesis should start from evidence in the user's own example, offer one testable interpretation, name the function without blame such as protection, prediction, relief, or cost, and ask whether the danger, need, or wording fits. Use the hypothesis timing checkpoint before asking a second or third deepening question: offer a hypothesis when one concrete episode, body cue, belief sentence, behavior, or mode voice is visible and the hypothesis would change the record shape, wording, links, or next action. Do not keep asking broad exploratory Psyche questions after the cue, meaning, protection, payoff, or cost is already visible. For behavior_pattern, belief_entry, mode_profile, mode_guide_session, and trigger_report, the next helpful move is usually one active formulation plus one correction question, not another passive reflection. Do not hypothesize yet when no concrete moment is visible, the user only wants a direct mechanical save, the user is flooded or unsafe, or the only available interpretation would be diagnosis-like, an origin story, or a certainty claim. Do not present schema, mode, belief, or pattern language as a verdict. If the user corrects the hypothesis, revise it once and move toward the saveable record shape instead of asking for another broad story.",
|
|
5463
5463
|
mixedIntentSequencingRule: "When one user message combines several Forge jobs, identify the primary job and the order of operations before asking a follow-up. If a read changes the truth of a later write, read first: Movement timeline or box detail before correction, Workbench run or node detail before editing or publishing, and Life Force overview before changing durable assumptions when the current energy picture is uncertain. If the user asks to understand and save Psyche material plus create a support record, formulate the primary Psyche record first, then derive the flashcard, note, link, task, or habit from the accepted wording. If the user already gave the concrete action, do not ask a broad lane question; say the product sequence briefly and ask only for the missing span, wording, flow, run, node, weekday, or link that changes the next action.",
|
|
5464
5464
|
duplicateDisambiguationRule: "Before creating or updating a normal stored entity when duplicate risk is plausible, search the shared batch entity route by entity type, distinctive title or wording, owner scope, and linked content. If a likely existing record appears, ask whether the user wants to update that record, link to it, or save a separate new record; do not reopen the whole create flow. For Psyche records, a similar belief, pattern, mode, trigger report, value, or flashcard is a formulation choice, not a duplicate error: compare the sentence, cue/payoff/cost, protective job, episode, urge sentence, or message and let the user choose update, link, or new version. For wiki_page and calendar_connection, use dedicated search/list/read routes before creating another page or connection. For Movement, Life Force, and Workbench, use the dedicated read lanes instead of batch duplicate search.",
|
|
5465
5465
|
destructiveActionRule: "Before deleting, archiving, invalidating, overwriting, disconnecting, or substantially replacing a Forge record or specialized object, confirm the exact target and what should remain understandable. Prefer normal soft-delete for stored entities unless the user explicitly asks for permanent removal. For Psyche records, preserve therapeutic history by asking whether the old belief, pattern, mode, trigger report, value, or flashcard should be updated, linked as history, archived, or kept distinct; do not delete it just because a cleaner formulation exists. For Movement, distinguish user-defined overlay deletion from automatic-box invalidation and stay/trip/point deletion, and read the specific span first when the target is uncertain. For calendar connections, Workbench flows, wiki pages, and questionnaire instruments, ask what downstream sync, published output, backlinks, run history, or completed runs should remain understandable before deleting or replacing the saved object.",
|
|
@@ -5880,7 +5880,13 @@ function buildV1Context(scope = {
|
|
|
5880
5880
|
].filter((value) => typeof value === "string" && value.length > 0)));
|
|
5881
5881
|
const habits = applyHabitScope(filterOwnedEntities("habit", listHabits(), scopedUserIdsForReads), scope, new Set(goals.map((goal) => goal.id)), new Set(tasks.map((task) => task.id)));
|
|
5882
5882
|
const strategies = applyStrategyScope(listStrategies({ userIds: scopedUserIdsForReads }), scope, new Set(goals.map((goal) => goal.id)));
|
|
5883
|
-
const dashboard = getDashboard({
|
|
5883
|
+
const dashboard = getDashboard({
|
|
5884
|
+
userIds: scopedUserIdsForReads,
|
|
5885
|
+
goals,
|
|
5886
|
+
projects,
|
|
5887
|
+
tasks,
|
|
5888
|
+
habits
|
|
5889
|
+
});
|
|
5884
5890
|
const selectedUsers = validScopedUserIds !== undefined
|
|
5885
5891
|
? users.filter((user) => validScopedUserIds.includes(user.id))
|
|
5886
5892
|
: users;
|
|
@@ -5892,9 +5898,7 @@ function buildV1Context(scope = {
|
|
|
5892
5898
|
backend: "forge-node-runtime",
|
|
5893
5899
|
mode: "transitional-node"
|
|
5894
5900
|
},
|
|
5895
|
-
metrics:
|
|
5896
|
-
userIds: scopedUserIdsForReads
|
|
5897
|
-
}),
|
|
5901
|
+
metrics: dashboard.gamification,
|
|
5898
5902
|
dashboard,
|
|
5899
5903
|
overview: getOverviewContext(now, {
|
|
5900
5904
|
userIds: scopedUserIdsForReads,
|
|
@@ -5932,6 +5936,55 @@ function buildV1Context(scope = {
|
|
|
5932
5936
|
lifeForce: buildLifeForcePayload(now, scopedUserIdsForReads)
|
|
5933
5937
|
};
|
|
5934
5938
|
}
|
|
5939
|
+
function compactV1ContextForShell(context) {
|
|
5940
|
+
const stripTaskPeople = (task) => {
|
|
5941
|
+
const { user: _user, ownerUser: _ownerUser, assignees: _assignees, ...compactTask } = task;
|
|
5942
|
+
return compactTask;
|
|
5943
|
+
};
|
|
5944
|
+
const stripOptionalTaskPeople = (task) => task ? stripTaskPeople(task) : task;
|
|
5945
|
+
return {
|
|
5946
|
+
...context,
|
|
5947
|
+
tasks: context.tasks.map(stripTaskPeople),
|
|
5948
|
+
dashboard: {
|
|
5949
|
+
...context.dashboard,
|
|
5950
|
+
projects: undefined,
|
|
5951
|
+
tasks: undefined,
|
|
5952
|
+
habits: undefined,
|
|
5953
|
+
tags: undefined,
|
|
5954
|
+
recentActivity: undefined,
|
|
5955
|
+
executionBuckets: context.dashboard.executionBuckets.map((bucket) => ({
|
|
5956
|
+
...bucket,
|
|
5957
|
+
tasks: bucket.tasks.map(stripTaskPeople)
|
|
5958
|
+
}))
|
|
5959
|
+
},
|
|
5960
|
+
overview: {
|
|
5961
|
+
...context.overview,
|
|
5962
|
+
topTasks: context.overview.topTasks.map(stripTaskPeople)
|
|
5963
|
+
},
|
|
5964
|
+
today: {
|
|
5965
|
+
...context.today,
|
|
5966
|
+
directive: {
|
|
5967
|
+
...context.today.directive,
|
|
5968
|
+
task: stripOptionalTaskPeople(context.today.directive.task)
|
|
5969
|
+
},
|
|
5970
|
+
timeline: context.today.timeline.map((bucket) => ({
|
|
5971
|
+
...bucket,
|
|
5972
|
+
tasks: bucket.tasks.map(stripTaskPeople)
|
|
5973
|
+
}))
|
|
5974
|
+
},
|
|
5975
|
+
risk: {
|
|
5976
|
+
...context.risk,
|
|
5977
|
+
overdueTasks: context.risk.overdueTasks.map(stripTaskPeople),
|
|
5978
|
+
blockedTasks: context.risk.blockedTasks.map(stripTaskPeople)
|
|
5979
|
+
},
|
|
5980
|
+
activity: undefined
|
|
5981
|
+
};
|
|
5982
|
+
}
|
|
5983
|
+
function shouldUseShellContextProfile(query) {
|
|
5984
|
+
const profile = typeof query.profile === "string" ? query.profile.trim() : "";
|
|
5985
|
+
const compact = typeof query.compact === "string" ? query.compact.trim() : query.compact;
|
|
5986
|
+
return profile === "shell" || compact === "1" || compact === true;
|
|
5987
|
+
}
|
|
5935
5988
|
function buildXpMetricsPayload(input = {}) {
|
|
5936
5989
|
const goals = input.goals ?? listGoals();
|
|
5937
5990
|
const tasks = input.tasks ?? listTasks();
|
|
@@ -7482,7 +7535,11 @@ export async function buildServer(options = {}) {
|
|
|
7482
7535
|
app.get("/api/v1/openapi.json", async () => buildOpenApiDocument());
|
|
7483
7536
|
app.get("/api/v1/context", async (request) => {
|
|
7484
7537
|
const auth = authenticateRequest(request.headers);
|
|
7485
|
-
|
|
7538
|
+
const query = request.query;
|
|
7539
|
+
const context = buildV1Context(resolveEffectiveReadScope(query, auth));
|
|
7540
|
+
return shouldUseShellContextProfile(query)
|
|
7541
|
+
? compactV1ContextForShell(context)
|
|
7542
|
+
: context;
|
|
7486
7543
|
});
|
|
7487
7544
|
app.get("/api/v1/life-force", async (request) => ({
|
|
7488
7545
|
lifeForce: buildLifeForcePayload(new Date(), resolveScopedUserIds(request.query)),
|
|
@@ -7586,7 +7643,10 @@ export async function buildServer(options = {}) {
|
|
|
7586
7643
|
return detail;
|
|
7587
7644
|
});
|
|
7588
7645
|
app.get("/api/v1/health/fitness", async (request) => ({
|
|
7589
|
-
fitness: getFitnessViewData(resolveScopedUserIds(request.query)
|
|
7646
|
+
fitness: getFitnessViewData(resolveScopedUserIds(request.query), {
|
|
7647
|
+
compact: request.query.compact === "1" ||
|
|
7648
|
+
request.query.compact === "true"
|
|
7649
|
+
})
|
|
7590
7650
|
}));
|
|
7591
7651
|
app.get("/api/v1/health/training-load", async (request) => ({
|
|
7592
7652
|
trainingLoad: getTrainingLoadViewData(resolveScopedUserIds(request.query))
|
|
@@ -7608,11 +7668,15 @@ export async function buildServer(options = {}) {
|
|
|
7608
7668
|
z.string().datetime().safeParse(query.dayEndAt).success
|
|
7609
7669
|
? query.dayEndAt
|
|
7610
7670
|
: undefined;
|
|
7671
|
+
const timeZone = typeof query.timeZone === "string" && query.timeZone.trim().length > 0
|
|
7672
|
+
? query.timeZone
|
|
7673
|
+
: undefined;
|
|
7611
7674
|
return {
|
|
7612
7675
|
weightLoss: getWeightLossViewData(resolveScopedUserIds(query), {
|
|
7613
7676
|
dateKey,
|
|
7614
7677
|
dayStartAt,
|
|
7615
|
-
dayEndAt
|
|
7678
|
+
dayEndAt,
|
|
7679
|
+
timeZone
|
|
7616
7680
|
})
|
|
7617
7681
|
};
|
|
7618
7682
|
});
|
|
@@ -8245,13 +8309,39 @@ export async function buildServer(options = {}) {
|
|
|
8245
8309
|
error: "Recorded moves are immutable in product UI. Create or edit a user-defined movement box instead."
|
|
8246
8310
|
};
|
|
8247
8311
|
});
|
|
8312
|
+
function estimatedJsonBytes(value) {
|
|
8313
|
+
try {
|
|
8314
|
+
return Buffer.byteLength(JSON.stringify(value ?? null), "utf8");
|
|
8315
|
+
}
|
|
8316
|
+
catch {
|
|
8317
|
+
return 0;
|
|
8318
|
+
}
|
|
8319
|
+
}
|
|
8320
|
+
function watchRouteMeasurement(operation, startedAt, requestBody, responseBody, extra = {}) {
|
|
8321
|
+
return {
|
|
8322
|
+
operation,
|
|
8323
|
+
backendDurationMs: Number((Number(process.hrtime.bigint() - startedAt) / 1_000_000).toFixed(2)),
|
|
8324
|
+
requestBytes: estimatedJsonBytes(requestBody),
|
|
8325
|
+
responseBytes: estimatedJsonBytes(responseBody),
|
|
8326
|
+
...extra
|
|
8327
|
+
};
|
|
8328
|
+
}
|
|
8248
8329
|
app.post("/api/v1/mobile/watch/bootstrap", async (request) => {
|
|
8330
|
+
const startedAt = process.hrtime.bigint();
|
|
8249
8331
|
const parsed = mobileWatchBootstrapSchema.parse(request.body ?? {});
|
|
8250
8332
|
const pairing = requireValidPairing(parsed.sessionId, parsed.pairingToken);
|
|
8251
8333
|
assertWatchReady(pairing);
|
|
8252
|
-
|
|
8334
|
+
const responseBody = {
|
|
8253
8335
|
watch: buildWatchBootstrap(pairing)
|
|
8254
8336
|
};
|
|
8337
|
+
return {
|
|
8338
|
+
...responseBody,
|
|
8339
|
+
measurement: watchRouteMeasurement("watch.bootstrap", startedAt, request.body ?? {}, responseBody, {
|
|
8340
|
+
surfaceCount: responseBody.watch.surfaces.length,
|
|
8341
|
+
habitCount: responseBody.watch.habits.length,
|
|
8342
|
+
promptCount: responseBody.watch.pendingPrompts.length
|
|
8343
|
+
})
|
|
8344
|
+
};
|
|
8255
8345
|
});
|
|
8256
8346
|
app.post("/api/v1/mobile/watch/habits/:id/check-ins", async (request, reply) => {
|
|
8257
8347
|
const parsed = mobileWatchHabitCheckInSchema.parse(request.body ?? {});
|
|
@@ -8277,22 +8367,45 @@ export async function buildServer(options = {}) {
|
|
|
8277
8367
|
};
|
|
8278
8368
|
});
|
|
8279
8369
|
app.post("/api/v1/mobile/watch/capture-events:batch", async (request) => {
|
|
8370
|
+
const startedAt = process.hrtime.bigint();
|
|
8280
8371
|
const parsed = mobileWatchCaptureBatchSchema.parse(request.body ?? {});
|
|
8281
8372
|
const pairing = requireValidPairing(parsed.sessionId, parsed.pairingToken);
|
|
8282
8373
|
assertWatchReady(pairing);
|
|
8283
|
-
|
|
8284
|
-
|
|
8374
|
+
const receipt = ingestWatchCaptureBatch(pairing, parsed);
|
|
8375
|
+
const responseBody = {
|
|
8376
|
+
receipt,
|
|
8285
8377
|
watch: buildWatchBootstrap(pairing)
|
|
8286
8378
|
};
|
|
8379
|
+
return {
|
|
8380
|
+
...responseBody,
|
|
8381
|
+
measurement: watchRouteMeasurement("watch.capture_batch", startedAt, request.body ?? {}, responseBody, {
|
|
8382
|
+
eventCount: parsed.events.length,
|
|
8383
|
+
storedCount: receipt.storedCount,
|
|
8384
|
+
duplicateCount: receipt.duplicateCount,
|
|
8385
|
+
projectedCount: receipt.projectedCount,
|
|
8386
|
+
projectionFailedCount: receipt.projectionFailedCount
|
|
8387
|
+
})
|
|
8388
|
+
};
|
|
8287
8389
|
});
|
|
8288
8390
|
app.post("/api/v1/mobile/watch/actions:batch", async (request) => {
|
|
8391
|
+
const startedAt = process.hrtime.bigint();
|
|
8289
8392
|
const parsed = mobileWatchCommandBatchSchema.parse(request.body ?? {});
|
|
8290
8393
|
const pairing = requireValidPairing(parsed.sessionId, parsed.pairingToken);
|
|
8291
8394
|
assertWatchReady(pairing);
|
|
8292
|
-
|
|
8293
|
-
|
|
8395
|
+
const receipt = ingestWatchCommandBatch(pairing, parsed);
|
|
8396
|
+
const responseBody = {
|
|
8397
|
+
receipt,
|
|
8294
8398
|
watch: buildWatchBootstrap(pairing)
|
|
8295
8399
|
};
|
|
8400
|
+
return {
|
|
8401
|
+
...responseBody,
|
|
8402
|
+
measurement: watchRouteMeasurement("watch.action_batch", startedAt, request.body ?? {}, responseBody, {
|
|
8403
|
+
commandCount: parsed.commands.length,
|
|
8404
|
+
processedCount: receipt.processedCount,
|
|
8405
|
+
replayedCount: receipt.replayedCount,
|
|
8406
|
+
failedCount: receipt.failedCount
|
|
8407
|
+
})
|
|
8408
|
+
};
|
|
8296
8409
|
});
|
|
8297
8410
|
app.post("/api/v1/mobile/healthkit/sync-sessions", async (request) => ({
|
|
8298
8411
|
upload: startMobileHealthSyncSession(mobileHealthSyncSessionStartSchema.parse(request.body ?? {}))
|
|
@@ -8302,7 +8415,19 @@ export async function buildServer(options = {}) {
|
|
|
8302
8415
|
const query = z
|
|
8303
8416
|
.object({
|
|
8304
8417
|
sessionId: z.string().trim().min(1),
|
|
8305
|
-
pairingToken: z.string().trim().min(1)
|
|
8418
|
+
pairingToken: z.string().trim().min(1),
|
|
8419
|
+
includeReceivedChunkIds: z
|
|
8420
|
+
.enum(["true", "false"])
|
|
8421
|
+
.optional()
|
|
8422
|
+
.transform((value) => value !== "false"),
|
|
8423
|
+
includeWorkoutImportExternalUids: z
|
|
8424
|
+
.enum(["true", "false"])
|
|
8425
|
+
.optional()
|
|
8426
|
+
.transform((value) => value !== "false"),
|
|
8427
|
+
includeWorkoutImportState: z
|
|
8428
|
+
.enum(["true", "false"])
|
|
8429
|
+
.optional()
|
|
8430
|
+
.transform((value) => value !== "false")
|
|
8306
8431
|
})
|
|
8307
8432
|
.parse(request.query ?? {});
|
|
8308
8433
|
return {
|
|
@@ -107,6 +107,7 @@ export const nutritionDailyActiveCaloriesUpdateSchema = z.object({
|
|
|
107
107
|
.string()
|
|
108
108
|
.regex(/^\d{4}-\d{2}-\d{2}$/)
|
|
109
109
|
.optional(),
|
|
110
|
+
timeZone: z.string().trim().optional(),
|
|
110
111
|
activeCaloriesKcal: z
|
|
111
112
|
.union([z.null(), z.coerce.number().finite().min(0)])
|
|
112
113
|
.optional(),
|
|
@@ -120,6 +121,7 @@ export const nutritionFoodLogCreateSchema = z.object({
|
|
|
120
121
|
.regex(/^\d{4}-\d{2}-\d{2}$/)
|
|
121
122
|
.nullable()
|
|
122
123
|
.optional(),
|
|
124
|
+
timeZone: z.string().trim().optional(),
|
|
123
125
|
mealLabel: z.string().trim().default(""),
|
|
124
126
|
source: z
|
|
125
127
|
.enum(["manual", "search", "barcode", "chatgpt", "photo", "saved_meal"])
|
|
@@ -242,8 +244,47 @@ function newId(prefix) {
|
|
|
242
244
|
function dayKey(value) {
|
|
243
245
|
return value.slice(0, 10);
|
|
244
246
|
}
|
|
245
|
-
function
|
|
246
|
-
|
|
247
|
+
function resolveTimeZone(timeZone) {
|
|
248
|
+
const candidate = timeZone?.trim();
|
|
249
|
+
if (!candidate) {
|
|
250
|
+
return Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC";
|
|
251
|
+
}
|
|
252
|
+
try {
|
|
253
|
+
new Intl.DateTimeFormat("en-US", { timeZone: candidate }).format(new Date());
|
|
254
|
+
return candidate;
|
|
255
|
+
}
|
|
256
|
+
catch {
|
|
257
|
+
return Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC";
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
function localDateParts(value, timeZone) {
|
|
261
|
+
const formatter = new Intl.DateTimeFormat("en-CA", {
|
|
262
|
+
timeZone,
|
|
263
|
+
year: "numeric",
|
|
264
|
+
month: "2-digit",
|
|
265
|
+
day: "2-digit",
|
|
266
|
+
hour: "2-digit",
|
|
267
|
+
minute: "2-digit",
|
|
268
|
+
second: "2-digit",
|
|
269
|
+
hour12: false
|
|
270
|
+
});
|
|
271
|
+
const parts = formatter.formatToParts(new Date(value));
|
|
272
|
+
const read = (type) => parts.find((part) => part.type === type)?.value ?? "00";
|
|
273
|
+
return {
|
|
274
|
+
year: read("year"),
|
|
275
|
+
month: read("month"),
|
|
276
|
+
day: read("day"),
|
|
277
|
+
hour: read("hour"),
|
|
278
|
+
minute: read("minute"),
|
|
279
|
+
second: read("second")
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
function localDateKey(value, timeZone) {
|
|
283
|
+
const parts = localDateParts(value, resolveTimeZone(timeZone));
|
|
284
|
+
return `${parts.year}-${parts.month}-${parts.day}`;
|
|
285
|
+
}
|
|
286
|
+
function normalizeDayKey(value, fallbackIso, timeZone) {
|
|
287
|
+
return value ?? localDateKey(fallbackIso, resolveTimeZone(timeZone));
|
|
247
288
|
}
|
|
248
289
|
function jsonString(value) {
|
|
249
290
|
return JSON.stringify(value ?? null);
|
|
@@ -1058,7 +1099,7 @@ export function createNutritionFoodLog(input) {
|
|
|
1058
1099
|
const parsed = nutritionFoodLogCreateSchema.parse(input);
|
|
1059
1100
|
const userId = resolveWriteUser(parsed.userId);
|
|
1060
1101
|
const loggedAt = parsed.loggedAt ?? nowIso();
|
|
1061
|
-
const logDayKey = normalizeDayKey(parsed.dayKey, loggedAt);
|
|
1102
|
+
const logDayKey = normalizeDayKey(parsed.dayKey, loggedAt, parsed.timeZone);
|
|
1062
1103
|
const id = newId("meal");
|
|
1063
1104
|
const now = nowIso();
|
|
1064
1105
|
runInTransaction(() => {
|
|
@@ -1084,7 +1125,7 @@ export function patchNutritionFoodLog(logId, input) {
|
|
|
1084
1125
|
return null;
|
|
1085
1126
|
}
|
|
1086
1127
|
const nextLoggedAt = parsed.loggedAt ?? existing.loggedAt;
|
|
1087
|
-
const nextDayKey = normalizeDayKey(parsed.dayKey, nextLoggedAt);
|
|
1128
|
+
const nextDayKey = normalizeDayKey(parsed.dayKey, nextLoggedAt, parsed.timeZone);
|
|
1088
1129
|
const nextConfirmationState = parsed.confirmationState ?? existing.confirmationState;
|
|
1089
1130
|
const now = nowIso();
|
|
1090
1131
|
runInTransaction(() => {
|
|
@@ -1168,7 +1209,7 @@ export function updateNutritionTarget(input) {
|
|
|
1168
1209
|
export function updateNutritionDailyActiveCalories(input) {
|
|
1169
1210
|
const parsed = nutritionDailyActiveCaloriesUpdateSchema.parse(input);
|
|
1170
1211
|
const userId = resolveWriteUser(parsed.userId);
|
|
1171
|
-
const dateKey = parsed.dayKey ?? new Date()
|
|
1212
|
+
const dateKey = parsed.dayKey ?? localDateKey(new Date(), resolveTimeZone(parsed.timeZone));
|
|
1172
1213
|
const now = nowIso();
|
|
1173
1214
|
if (parsed.activeCaloriesKcal == null) {
|
|
1174
1215
|
getDatabase()
|
|
@@ -1682,7 +1723,8 @@ function buildGeneratedHypotheses(logs, subjective, gut, appearance) {
|
|
|
1682
1723
|
export function getWeightLossViewData(userIds, options = {}) {
|
|
1683
1724
|
const generatedAt = new Date().toISOString();
|
|
1684
1725
|
const userId = resolveReadUser(userIds);
|
|
1685
|
-
const
|
|
1726
|
+
const timeZone = resolveTimeZone(options.timeZone);
|
|
1727
|
+
const todayKey = options.dateKey ?? localDateKey(generatedAt, timeZone);
|
|
1686
1728
|
const targetRow = getDatabase()
|
|
1687
1729
|
.prepare(`SELECT * FROM nutrition_targets WHERE user_id = ?`)
|
|
1688
1730
|
.get(userId);
|