forge-openclaw-plugin 0.3.12 → 0.3.14
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-BFjWjRIM.js → action-bar-B9MYlps2.js} +1 -1
- package/dist/assets/{activity-page-D-6yBWuZ.js → activity-page-DbzbChcE.js} +1 -1
- package/dist/assets/{ai-surface-workspace-BEfo9bRO.js → ai-surface-workspace-CF0257Hs.js} +1 -1
- package/dist/assets/{atlas-panel-BfMyJXxQ.js → atlas-panel-CO3RYAKn.js} +1 -1
- package/dist/assets/{calendar-page-D4tQNJ2V.js → calendar-page-BuuHKEHC.js} +1 -1
- package/dist/assets/{calendar-rules-C-6O_uGU.js → calendar-rules-DKftgNx5.js} +1 -1
- package/dist/assets/{calendar-week-toolbar-_NzeKsYx.js → calendar-week-toolbar-ChIpkT-G.js} +1 -1
- package/dist/assets/{companion-sync-lab-page-D1Oqsf6M.js → companion-sync-lab-page-BZRX4Btw.js} +1 -1
- package/dist/assets/{daily-metrics-dashboard-DtE3pVOl.js → daily-metrics-dashboard-CXDsaAQd.js} +1 -1
- package/dist/assets/{define-workbench-box-D-32C8nM.js → define-workbench-box-CpG0Zb1L.js} +1 -1
- package/dist/assets/{entity-link-multiselect-DcCvkesQ.js → entity-link-multiselect-Dl4rZqdg.js} +1 -1
- package/dist/assets/{entity-note-count-link-Cjsk5oT2.js → entity-note-count-link-Bs1aKYyD.js} +1 -1
- package/dist/assets/{entity-notes-surface-DQQPLjxd.js → entity-notes-surface-B56XSw7M.js} +1 -1
- package/dist/assets/{execution-board-agWQbN-y.js → execution-board-D66C_ikW.js} +1 -1
- package/dist/assets/{faceted-token-search-DldM3-ru.js → faceted-token-search-Dg2rjknH.js} +1 -1
- package/dist/assets/{flagship-signal-deck-BlLYW9Kz.js → flagship-signal-deck-C6KVPhmM.js} +1 -1
- package/dist/assets/{floating-action-menu-D9-psbha.js → floating-action-menu-DAFAEBcA.js} +1 -1
- package/dist/assets/{generic-node-view-CcepUVhP.js → generic-node-view-C6DK5hJ6.js} +1 -1
- package/dist/assets/{goal-detail-page-DP1n5-Hk.js → goal-detail-page-CC4VXud6.js} +1 -1
- package/dist/assets/{goal-dialog-Oxx8WqbZ.js → goal-dialog-15hD8EBp.js} +1 -1
- package/dist/assets/{goals-page-CUt1a4Y2.js → goals-page-CQ2lJMzI.js} +1 -1
- package/dist/assets/{habits-page-5REbWAlo.js → habits-page-a7KVPaQp.js} +1 -1
- package/dist/assets/{health-boxes-sHNML3tm.js → health-boxes-DqgvIYoL.js} +1 -1
- package/dist/assets/index-ClJbJhca.css +1 -0
- package/dist/assets/index-FpGANF9S.js +2 -0
- package/dist/assets/{inline-note-fields-Bql_KfR9.js → inline-note-fields-COgzxr_7.js} +1 -1
- package/dist/assets/{insight-flow-dialog-CN-CMSB7.js → insight-flow-dialog-Dmb6NSGp.js} +1 -1
- package/dist/assets/{insights-page-B1u6ONtQ.js → insights-page-OnqR4cYI.js} +1 -1
- package/dist/assets/{kanban-boxes-DB1kuUlY.js → kanban-boxes-BWUzntCV.js} +1 -1
- package/dist/assets/{kanban-page-BBW9_vMu.js → kanban-page-BI16Gzp_.js} +1 -1
- package/dist/assets/{knowledge-graph-page-CoJaydZb.js → knowledge-graph-page-DxEBaEke.js} +1 -1
- package/dist/assets/{life-force-page-DBYbA1GF.js → life-force-page-CDEXEQai.js} +1 -1
- package/dist/assets/{life-force-workspace-BiD9xOEt.js → life-force-workspace-C7UOnJEf.js} +1 -1
- package/dist/assets/{metric-tile-CuP9DOYm.js → metric-tile-boeHB1R1.js} +1 -1
- package/dist/assets/{movement-boxes-DZg_qPPg.js → movement-boxes-BUSqaTL2.js} +1 -1
- package/dist/assets/{movement-page-CmwsQGR_.js → movement-page-DcbO0497.js} +1 -1
- package/dist/assets/note-markdown-DXXI3W3V.js +3 -0
- package/dist/assets/{note-tags-input-C_x5WdK5.js → note-tags-input-CYh3TVW2.js} +1 -1
- package/dist/assets/{notes-boxes-BEFlp9yd.js → notes-boxes-CMJXX2K0.js} +1 -1
- package/dist/assets/{notes-page-B6Vl-GPf.js → notes-page-DvHMcQey.js} +1 -1
- package/dist/assets/{open-in-graph-button-C-bJekoH.js → open-in-graph-button-4UYrp1XP.js} +1 -1
- package/dist/assets/{orbit-map-DHeTM15g.js → orbit-map-BwK7sDaC.js} +1 -1
- package/dist/assets/{overview-page-BRWje1F9.js → overview-page-Z5vaUTm3.js} +1 -1
- package/dist/assets/{page-hero-8bITsx_x.js → page-hero-DvrM83_C.js} +1 -1
- package/dist/assets/{pill-cluster-XQjm-wPc.js → pill-cluster-DYI3Ibj8.js} +1 -1
- package/dist/assets/{preference-entity-handoff-button-DwYF_5i3.js → preference-entity-handoff-button-C2ATjvws.js} +1 -1
- package/dist/assets/{preferences-page-C7DBPpNb.js → preferences-page-BAexXHye.js} +1 -1
- package/dist/assets/{project-collections-mtxanSMf.js → project-collections-B9nr-Ts-.js} +1 -1
- package/dist/assets/{project-detail-page-BT87Goqc.js → project-detail-page-B9PqyPu9.js} +1 -1
- package/dist/assets/{project-dialog-DnZe757y.js → project-dialog-CBA-D65n.js} +1 -1
- package/dist/assets/{project-management-hierarchy-page-B3R2lNFI.js → project-management-hierarchy-page-DXK14jn0.js} +1 -1
- package/dist/assets/{project-management-section-nav-DyBWxHbe.js → project-management-section-nav-DJ3QKCtr.js} +1 -1
- package/dist/assets/{projects-boxes-CxZj3P29.js → projects-boxes-iBu_PRqe.js} +1 -1
- package/dist/assets/{projects-page-Bec11c0x.js → projects-page-CdAk-ByT.js} +1 -1
- package/dist/assets/{psyche-behaviors-page-DWRpYvl1.js → psyche-behaviors-page-CbhhTfU2.js} +1 -1
- package/dist/assets/{psyche-flashcards-page-CX4rcsXZ.js → psyche-flashcards-page-DQaw_vUQ.js} +1 -1
- package/dist/assets/{psyche-goal-map-page-Y6b3lCvV.js → psyche-goal-map-page-C-ZTVOEP.js} +1 -1
- package/dist/assets/{psyche-graph-CQuCWKIp.js → psyche-graph-DYzeClxn.js} +1 -1
- package/dist/assets/{psyche-metrics-page-DadDJOnm.js → psyche-metrics-page-C9hKn10A.js} +1 -1
- package/dist/assets/{psyche-mode-guide-page-B1nz0uCg.js → psyche-mode-guide-page-CR8e984W.js} +1 -1
- package/dist/assets/{psyche-modes-page-i3uSuhKA.js → psyche-modes-page-lQdpAcY3.js} +1 -1
- package/dist/assets/{psyche-page-Y_s-BE2m.js → psyche-page-CTdIDkw9.js} +1 -1
- package/dist/assets/{psyche-patterns-page-DaaOLIlN.js → psyche-patterns-page-Drgm-f7I.js} +1 -1
- package/dist/assets/{psyche-questionnaire-builder-page-CuF7rXOv.js → psyche-questionnaire-builder-page-gRwdGXde.js} +1 -1
- package/dist/assets/{psyche-questionnaire-detail-page-BfFEMkRY.js → psyche-questionnaire-detail-page-CIP9b2UI.js} +1 -1
- package/dist/assets/{psyche-questionnaire-run-detail-page-BoQTvd7Q.js → psyche-questionnaire-run-detail-page-SYndwtF3.js} +1 -1
- package/dist/assets/{psyche-questionnaire-run-page-C0qKiNZN.js → psyche-questionnaire-run-page-CXiJyd5i.js} +1 -1
- package/dist/assets/{psyche-questionnaires-page-B6hfD448.js → psyche-questionnaires-page-CFPKwA3O.js} +1 -1
- package/dist/assets/{psyche-report-detail-page-BlFL8moM.js → psyche-report-detail-page-dU30a2WE.js} +1 -1
- package/dist/assets/{psyche-reports-page-DAAcYENp.js → psyche-reports-page-Cn0EBndy.js} +1 -1
- package/dist/assets/{psyche-schemas-beliefs-page-CsxKSrKM.js → psyche-schemas-beliefs-page-Bab4xSWv.js} +1 -1
- package/dist/assets/{psyche-screen-time-page-n4b0e58x.js → psyche-screen-time-page-lIe6GQxJ.js} +1 -1
- package/dist/assets/{psyche-self-observation-page-BaxEQ2-3.js → psyche-self-observation-page-BTE3KfIl.js} +1 -1
- package/dist/assets/{psyche-values-page-DTv5NMSU.js → psyche-values-page-DclBZ9xw.js} +1 -1
- package/dist/assets/question-flow-dialog-Ded2E85L.js +2 -0
- package/dist/assets/{report-chain-fields-D132-EMh.js → report-chain-fields-DY640iqL.js} +1 -1
- package/dist/assets/{rewards-page-C533lVP-.js → rewards-page-FxUXB76B.js} +1 -1
- package/dist/assets/{scheduling-rules-editor-CDpontGp.js → scheduling-rules-editor-jakFfxqF.js} +1 -1
- package/dist/assets/{schema-badge-B3DiMnjB.js → schema-badge-30B5syHA.js} +1 -1
- package/dist/assets/{schemas-CH1_ngUX.js → schemas-Db29G8NU.js} +1 -1
- package/dist/assets/{select-menu-BOF-k4Ln.js → select-menu-BF2zI3RW.js} +1 -1
- package/dist/assets/{settings-agents-page-B5OQtlZX.js → settings-agents-page-Bh-Bv6FQ.js} +1 -1
- package/dist/assets/{settings-bin-page-D_bk3Kcu.js → settings-bin-page-DT8JJero.js} +1 -1
- package/dist/assets/{settings-calendar-page-PuSj9_kM.js → settings-calendar-page-BA4_Qqiu.js} +3 -3
- package/dist/assets/{settings-data-page-EwFMaeq6.js → settings-data-page-K4kpmQJY.js} +1 -1
- package/dist/assets/{settings-logs-page-BKkse0DX.js → settings-logs-page-DkuNPAZo.js} +1 -1
- package/dist/assets/{settings-mobile-page-BrIVmdeB.js → settings-mobile-page-4pCNwD91.js} +1 -1
- package/dist/assets/{settings-models-page-Mg84D_0K.js → settings-models-page-CZHG3t7W.js} +1 -1
- package/dist/assets/{settings-page-D-kul92f.js → settings-page-B_Be0vOY.js} +1 -1
- package/dist/assets/{settings-rewards-page-waNyCcX_.js → settings-rewards-page-C6nFTWJu.js} +1 -1
- package/dist/assets/{settings-section-nav-BmJWnrYk.js → settings-section-nav-Lo-VKCfZ.js} +1 -1
- package/dist/assets/{settings-users-page-DBgC6y56.js → settings-users-page-CVzNp4-m.js} +1 -1
- package/dist/assets/{settings-wiki-page-CM0te9dI.js → settings-wiki-page-DXM--7BB.js} +1 -1
- package/dist/assets/sleep-page-DvPdZMKx.js +1 -0
- package/dist/assets/{sports-page-eg5Rfc_E.js → sports-page-DjuZlOur.js} +1 -1
- package/dist/assets/{strategies-page-D4AqvFNW.js → strategies-page-PE1IlatS.js} +1 -1
- package/dist/assets/{strategy-detail-page-BlYVkXaW.js → strategy-detail-page-C4KiEGCI.js} +1 -1
- package/dist/assets/{strategy-dialog-FO9Oa0dB.js → strategy-dialog-Cwf7Y3P5.js} +1 -1
- package/dist/assets/surface-ubuOfukq.js +1 -0
- package/dist/assets/{task-detail-page-Dy-aRyY6.js → task-detail-page-B3SMFZQ3.js} +1 -1
- package/dist/assets/{task-dialog-DrA9pba7.js → task-dialog-CI_Fy_FW.js} +1 -1
- package/dist/assets/{timebox-planning-dialog-C_gnfxFx.js → timebox-planning-dialog-BtFuVoA1.js} +1 -1
- package/dist/assets/{today-boxes-BFOws_iC.js → today-boxes-CYuxSkZf.js} +1 -1
- package/dist/assets/{today-page-BoPj6a6q.js → today-page-CHGpLEZ3.js} +1 -1
- package/dist/assets/{training-load-page-DGU40Zfl.js → training-load-page-GZJF-Yy5.js} +1 -1
- package/dist/assets/{user-badge-CZWtYeMw.js → user-badge-ByhC6bMU.js} +1 -1
- package/dist/assets/{utility-widgets-B3wWGxQc.js → utility-widgets-ilORjDmg.js} +1 -1
- package/dist/assets/{vitals-page-BXRZEP_8.js → vitals-page-mdEqIm_9.js} +1 -1
- package/dist/assets/{weekly-review-page-D5cebA5x.js → weekly-review-page-C8W-yg5r.js} +1 -1
- package/dist/assets/weight-loss-page-BApTknCn.js +5 -0
- package/dist/assets/{wiki-article-markdown-CvaCjg_t.js → wiki-article-markdown-DBccllQg.js} +1 -1
- package/dist/assets/{wiki-editor-page-Dco79SLY.js → wiki-editor-page-BfRfH1O3.js} +2 -2
- package/dist/assets/{wiki-ingest-history-page-uvKRkRDF.js → wiki-ingest-history-page-C-6H3MU6.js} +1 -1
- package/dist/assets/{wiki-ingest-modal-BW6AJPea.js → wiki-ingest-modal-DnlhByD_.js} +1 -1
- package/dist/assets/{wiki-page-BIL5zMqv.js → wiki-page-DPZ55e3x.js} +1 -1
- package/dist/assets/{workbench-flow-page-Dx_nq8R_.js → workbench-flow-page-_-NKIx5R.js} +1 -1
- package/dist/assets/{workbench-page-BJnt_Zzf.js → workbench-page-NIAzggwX.js} +1 -1
- package/dist/assets/{workout-detail-page-CKgqyB-y.js → workout-detail-page-3fxr6HL4.js} +1 -1
- package/dist/index.html +2 -2
- package/dist/server/server/src/app.js +8 -4
- package/dist/server/server/src/health-weight-loss.js +321 -14
- package/dist/server/server/src/managers/platform/openai-responses-provider.js +2 -2
- package/dist/server/server/src/openapi.js +26 -3
- package/dist/server/server/src/repositories/ai-connectors.js +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/skills/forge-openclaw/SKILL.md +22 -0
- package/skills/forge-openclaw/entity_conversation_playbooks.md +17 -0
- package/skills/forge-openclaw/psyche_entity_playbooks.md +7 -0
- package/dist/assets/index-CQ5r7ZUz.js +0 -2
- package/dist/assets/index-FxgNSuZX.css +0 -1
- package/dist/assets/note-markdown-B82ncnFt.js +0 -3
- package/dist/assets/question-flow-dialog-CskCt5NZ.js +0 -2
- package/dist/assets/sleep-page-C_krRE59.js +0 -1
- package/dist/assets/surface-MVeeZGKB.js +0 -1
- package/dist/assets/weight-loss-page-BuUdFh9z.js +0 -5
|
@@ -13,6 +13,9 @@ const scoreSchema = z
|
|
|
13
13
|
.union([z.coerce.number().int().min(0).max(10), z.null()])
|
|
14
14
|
.optional();
|
|
15
15
|
const tagsSchema = z.array(z.string().trim().min(1)).default([]);
|
|
16
|
+
const parsedStringDefaultSchema = z.preprocess((value) => (typeof value === "string" ? value : ""), z.string().trim());
|
|
17
|
+
const parsedOptionalDatetimeSchema = z.preprocess((value) => (typeof value === "string" && value.trim() ? value : undefined), z.string().datetime().optional());
|
|
18
|
+
const parsedOptionalNumberSchema = z.preprocess((value) => (value === null || value === "" ? undefined : value), z.coerce.number().finite().optional());
|
|
16
19
|
const linksSchema = z
|
|
17
20
|
.array(z.object({
|
|
18
21
|
entityType: z.string().trim().min(1),
|
|
@@ -2169,28 +2172,300 @@ function getNutritionCodexProfile(connectionId) {
|
|
|
2169
2172
|
}
|
|
2170
2173
|
const parsedMealItemSchema = z.object({
|
|
2171
2174
|
name: z.string().trim().min(1),
|
|
2175
|
+
searchQuery: parsedStringDefaultSchema.default(""),
|
|
2176
|
+
brand: parsedStringDefaultSchema.default(""),
|
|
2172
2177
|
quantity: z.coerce.number().positive().default(1),
|
|
2173
2178
|
unit: z.string().trim().min(1).default("serving"),
|
|
2174
|
-
grams:
|
|
2175
|
-
calories:
|
|
2176
|
-
proteinGrams:
|
|
2177
|
-
carbohydrateGrams:
|
|
2178
|
-
fatGrams:
|
|
2179
|
-
fiberGrams:
|
|
2180
|
-
sugarGrams:
|
|
2181
|
-
sodiumMg:
|
|
2179
|
+
grams: parsedOptionalNumberSchema,
|
|
2180
|
+
calories: parsedOptionalNumberSchema,
|
|
2181
|
+
proteinGrams: parsedOptionalNumberSchema,
|
|
2182
|
+
carbohydrateGrams: parsedOptionalNumberSchema,
|
|
2183
|
+
fatGrams: parsedOptionalNumberSchema,
|
|
2184
|
+
fiberGrams: parsedOptionalNumberSchema,
|
|
2185
|
+
sugarGrams: parsedOptionalNumberSchema,
|
|
2186
|
+
sodiumMg: parsedOptionalNumberSchema,
|
|
2182
2187
|
tags: tagsSchema,
|
|
2183
2188
|
confidence: z.coerce.number().min(0).max(1).default(0.45)
|
|
2184
2189
|
});
|
|
2185
2190
|
const parsedMealSchema = z.object({
|
|
2186
2191
|
mealLabel: z.string().trim().default(""),
|
|
2187
|
-
loggedAt:
|
|
2192
|
+
loggedAt: parsedOptionalDatetimeSchema,
|
|
2188
2193
|
items: z.array(parsedMealItemSchema).min(1),
|
|
2189
2194
|
uncertaintyReasons: z.array(z.string()).default([]),
|
|
2190
2195
|
clarificationQuestions: z.array(z.string()).default([]),
|
|
2191
2196
|
tags: tagsSchema
|
|
2192
2197
|
});
|
|
2198
|
+
const parsedMealNutritionCompletionSchema = z.object({
|
|
2199
|
+
items: z
|
|
2200
|
+
.array(z.object({
|
|
2201
|
+
index: z.coerce.number().int().min(0),
|
|
2202
|
+
grams: parsedOptionalNumberSchema,
|
|
2203
|
+
calories: parsedOptionalNumberSchema,
|
|
2204
|
+
proteinGrams: parsedOptionalNumberSchema,
|
|
2205
|
+
carbohydrateGrams: parsedOptionalNumberSchema,
|
|
2206
|
+
fatGrams: parsedOptionalNumberSchema,
|
|
2207
|
+
fiberGrams: parsedOptionalNumberSchema,
|
|
2208
|
+
sugarGrams: parsedOptionalNumberSchema,
|
|
2209
|
+
sodiumMg: parsedOptionalNumberSchema,
|
|
2210
|
+
confidence: z.coerce.number().min(0).max(1).default(0.45),
|
|
2211
|
+
reason: parsedStringDefaultSchema.default("")
|
|
2212
|
+
}))
|
|
2213
|
+
.default([]),
|
|
2214
|
+
validationNotes: z.array(z.string()).default([])
|
|
2215
|
+
});
|
|
2216
|
+
function searchableFoodText(value) {
|
|
2217
|
+
return (value ?? "")
|
|
2218
|
+
.trim()
|
|
2219
|
+
.toLowerCase()
|
|
2220
|
+
.normalize("NFKD")
|
|
2221
|
+
.replace(/[\u0300-\u036f]/g, "")
|
|
2222
|
+
.replace(/[^a-z0-9]+/g, " ")
|
|
2223
|
+
.replace(/\s+/g, " ")
|
|
2224
|
+
.trim();
|
|
2225
|
+
}
|
|
2226
|
+
function foodSearchTokens(value) {
|
|
2227
|
+
return searchableFoodText(value)
|
|
2228
|
+
.split(" ")
|
|
2229
|
+
.filter((token) => token.length >= 3);
|
|
2230
|
+
}
|
|
2231
|
+
function foodHasRequiredMacros(food) {
|
|
2232
|
+
return (hasNutritionValue(food.calories) &&
|
|
2233
|
+
hasNutritionValue(food.proteinGrams) &&
|
|
2234
|
+
hasNutritionValue(food.carbohydrateGrams) &&
|
|
2235
|
+
hasNutritionValue(food.fatGrams));
|
|
2236
|
+
}
|
|
2237
|
+
function scoreNutritionFoodMatch(item, food) {
|
|
2238
|
+
const queryText = searchableFoodText([item.searchQuery, item.brand, item.name].filter(Boolean).join(" "));
|
|
2239
|
+
const foodText = searchableFoodText([food.brand, food.name].join(" "));
|
|
2240
|
+
const queryTokens = foodSearchTokens(queryText);
|
|
2241
|
+
if (queryTokens.length === 0 || !foodText) {
|
|
2242
|
+
return 0;
|
|
2243
|
+
}
|
|
2244
|
+
const matchedTokens = queryTokens.filter((token) => foodText.includes(token));
|
|
2245
|
+
const coverage = matchedTokens.length / queryTokens.length;
|
|
2246
|
+
const exactBoost = queryText && foodText.includes(queryText)
|
|
2247
|
+
? 0.35
|
|
2248
|
+
: searchableFoodText(item.name) &&
|
|
2249
|
+
foodText.includes(searchableFoodText(item.name))
|
|
2250
|
+
? 0.2
|
|
2251
|
+
: 0;
|
|
2252
|
+
const nutritionBoost = foodHasRequiredMacros(food) ? 0.2 : -0.2;
|
|
2253
|
+
return coverage + exactBoost + nutritionBoost;
|
|
2254
|
+
}
|
|
2255
|
+
const countedFoodGramEstimates = [
|
|
2256
|
+
{ tokens: ["almond"], gramsPerUnit: 1.2 },
|
|
2257
|
+
{ tokens: ["cashew"], gramsPerUnit: 1.6 },
|
|
2258
|
+
{ tokens: ["pistachio"], gramsPerUnit: 0.6 },
|
|
2259
|
+
{ tokens: ["walnut"], gramsPerUnit: 4 },
|
|
2260
|
+
{ tokens: ["hazelnut"], gramsPerUnit: 1.3 },
|
|
2261
|
+
{ tokens: ["egg"], gramsPerUnit: 50 },
|
|
2262
|
+
{ tokens: ["slice", "bread"], gramsPerUnit: 35 }
|
|
2263
|
+
];
|
|
2264
|
+
function estimateCountedFoodGrams(item) {
|
|
2265
|
+
if (hasNutritionValue(item.grams) && item.grams > 0) {
|
|
2266
|
+
return item.grams;
|
|
2267
|
+
}
|
|
2268
|
+
if (!hasNutritionValue(item.quantity) || item.quantity <= 0) {
|
|
2269
|
+
return item.grams;
|
|
2270
|
+
}
|
|
2271
|
+
const unit = normalizeCustomFoodToken(item.unit);
|
|
2272
|
+
const countLikeUnits = new Set([
|
|
2273
|
+
"",
|
|
2274
|
+
"piece",
|
|
2275
|
+
"pieces",
|
|
2276
|
+
"unit",
|
|
2277
|
+
"units",
|
|
2278
|
+
"count",
|
|
2279
|
+
"counts",
|
|
2280
|
+
"custom",
|
|
2281
|
+
"almond",
|
|
2282
|
+
"almonds",
|
|
2283
|
+
"nut",
|
|
2284
|
+
"nuts"
|
|
2285
|
+
]);
|
|
2286
|
+
if (!countLikeUnits.has(unit)) {
|
|
2287
|
+
return item.grams;
|
|
2288
|
+
}
|
|
2289
|
+
const text = searchableFoodText([item.name, item.searchQuery, item.brand].filter(Boolean).join(" "));
|
|
2290
|
+
const estimate = countedFoodGramEstimates.find(({ tokens }) => tokens.every((token) => text.includes(token)));
|
|
2291
|
+
return estimate ? round(item.quantity * estimate.gramsPerUnit, 1) : item.grams;
|
|
2292
|
+
}
|
|
2293
|
+
async function resolveParsedMealItemNutrition(item) {
|
|
2294
|
+
const searchQueries = Array.from(new Set([
|
|
2295
|
+
item.searchQuery,
|
|
2296
|
+
[item.brand, item.name].filter(Boolean).join(" "),
|
|
2297
|
+
item.name
|
|
2298
|
+
]
|
|
2299
|
+
.map((query) => query.trim())
|
|
2300
|
+
.filter(Boolean)));
|
|
2301
|
+
const seen = new Set();
|
|
2302
|
+
let match = null;
|
|
2303
|
+
for (const query of searchQueries) {
|
|
2304
|
+
const foods = await searchNutritionFoods({ query, limit: 1 })
|
|
2305
|
+
.then((result) => result.foods)
|
|
2306
|
+
.catch(() => []);
|
|
2307
|
+
const candidates = foods
|
|
2308
|
+
.filter((food) => {
|
|
2309
|
+
if (seen.has(food.id)) {
|
|
2310
|
+
return false;
|
|
2311
|
+
}
|
|
2312
|
+
seen.add(food.id);
|
|
2313
|
+
return foodHasRequiredMacros(food);
|
|
2314
|
+
})
|
|
2315
|
+
.map((food) => ({
|
|
2316
|
+
food,
|
|
2317
|
+
score: scoreNutritionFoodMatch(item, food)
|
|
2318
|
+
}))
|
|
2319
|
+
.sort((left, right) => right.score - left.score);
|
|
2320
|
+
match = candidates.find((candidate) => candidate.score >= 0.45) ?? null;
|
|
2321
|
+
if (match) {
|
|
2322
|
+
break;
|
|
2323
|
+
}
|
|
2324
|
+
}
|
|
2325
|
+
const baseItem = {
|
|
2326
|
+
name: item.name,
|
|
2327
|
+
brand: item.brand || null,
|
|
2328
|
+
quantity: item.quantity,
|
|
2329
|
+
unit: item.unit,
|
|
2330
|
+
grams: estimateCountedFoodGrams(item),
|
|
2331
|
+
calories: item.calories,
|
|
2332
|
+
proteinGrams: item.proteinGrams,
|
|
2333
|
+
carbohydrateGrams: item.carbohydrateGrams,
|
|
2334
|
+
fatGrams: item.fatGrams,
|
|
2335
|
+
fiberGrams: item.fiberGrams,
|
|
2336
|
+
sugarGrams: item.sugarGrams,
|
|
2337
|
+
sodiumMg: item.sodiumMg,
|
|
2338
|
+
tags: item.tags,
|
|
2339
|
+
confidence: item.confidence,
|
|
2340
|
+
nutrients: {}
|
|
2341
|
+
};
|
|
2342
|
+
if (match) {
|
|
2343
|
+
const resolved = resolveMealItemForInsert({
|
|
2344
|
+
...baseItem,
|
|
2345
|
+
foodId: match.food.id,
|
|
2346
|
+
name: match.food.name,
|
|
2347
|
+
brand: match.food.brand || item.brand || null,
|
|
2348
|
+
tags: Array.from(new Set([
|
|
2349
|
+
...item.tags,
|
|
2350
|
+
"catalog_resolved",
|
|
2351
|
+
match.food.source.replaceAll("-", "_")
|
|
2352
|
+
])),
|
|
2353
|
+
confidence: Math.max(item.confidence, match.food.confidence ?? 0.65),
|
|
2354
|
+
nutrients: {
|
|
2355
|
+
catalogResolution: {
|
|
2356
|
+
source: match.food.source,
|
|
2357
|
+
sourceId: match.food.sourceId,
|
|
2358
|
+
searchQueries,
|
|
2359
|
+
score: round(match.score, 2)
|
|
2360
|
+
}
|
|
2361
|
+
}
|
|
2362
|
+
}, { cacheConfirmedCustomFood: false });
|
|
2363
|
+
return {
|
|
2364
|
+
...resolved,
|
|
2365
|
+
resolutionNote: `Resolved "${item.name}" to ${match.food.name}${match.food.brand ? ` (${match.food.brand})` : ""}.`
|
|
2366
|
+
};
|
|
2367
|
+
}
|
|
2368
|
+
return {
|
|
2369
|
+
...baseItem,
|
|
2370
|
+
tags: Array.from(new Set([...item.tags, "chatgpt_estimate"])),
|
|
2371
|
+
nutrients: {
|
|
2372
|
+
estimateResolution: {
|
|
2373
|
+
searchQueries,
|
|
2374
|
+
searchedSources: ["local_cache", "open_food_facts", "usda_fdc"],
|
|
2375
|
+
matched: false
|
|
2376
|
+
}
|
|
2377
|
+
},
|
|
2378
|
+
resolutionNote: searchQueries.length > 0
|
|
2379
|
+
? `No catalog match with complete macros for "${item.name}"; using ChatGPT estimate for review.`
|
|
2380
|
+
: null
|
|
2381
|
+
};
|
|
2382
|
+
}
|
|
2383
|
+
async function completeMissingParsedMealNutritionWithChatGpt(rawText, items, llm, profile) {
|
|
2384
|
+
const incompleteItems = items
|
|
2385
|
+
.map((item, index) => ({ item, index, missing: missingRequiredNutritionFields(item) }))
|
|
2386
|
+
.filter(({ missing }) => missing.length > 0);
|
|
2387
|
+
if (incompleteItems.length === 0) {
|
|
2388
|
+
return { items, validationNotes: [], llmCallCount: 0 };
|
|
2389
|
+
}
|
|
2390
|
+
const result = await llm.runTextPrompt(profile, {
|
|
2391
|
+
systemPrompt: "You are Forge's nutrition fact checker. Return strict JSON only. Use conservative, plausible nutrition values and explain uncertainty briefly.",
|
|
2392
|
+
prompt: `Some parsed food-log items still lack required nutrition after Forge searched its local, Open Food Facts, and USDA-backed catalogs.
|
|
2393
|
+
|
|
2394
|
+
Original user text:
|
|
2395
|
+
${rawText}
|
|
2396
|
+
|
|
2397
|
+
Items needing complete nutrition:
|
|
2398
|
+
${JSON.stringify(incompleteItems.map(({ item, index, missing }) => ({
|
|
2399
|
+
index,
|
|
2400
|
+
name: item.name,
|
|
2401
|
+
brand: item.brand,
|
|
2402
|
+
quantity: item.quantity,
|
|
2403
|
+
unit: item.unit,
|
|
2404
|
+
grams: item.grams,
|
|
2405
|
+
missing
|
|
2406
|
+
})))}
|
|
2407
|
+
|
|
2408
|
+
Return strict JSON only:
|
|
2409
|
+
{
|
|
2410
|
+
"items": [
|
|
2411
|
+
{
|
|
2412
|
+
"index": 0,
|
|
2413
|
+
"grams": 100,
|
|
2414
|
+
"calories": 0,
|
|
2415
|
+
"proteinGrams": 0,
|
|
2416
|
+
"carbohydrateGrams": 0,
|
|
2417
|
+
"fatGrams": 0,
|
|
2418
|
+
"fiberGrams": null,
|
|
2419
|
+
"sugarGrams": null,
|
|
2420
|
+
"sodiumMg": null,
|
|
2421
|
+
"confidence": 0.0,
|
|
2422
|
+
"reason": "short source/estimate explanation"
|
|
2423
|
+
}
|
|
2424
|
+
],
|
|
2425
|
+
"validationNotes": []
|
|
2426
|
+
}
|
|
2427
|
+
|
|
2428
|
+
Every returned item must include calories, proteinGrams, carbohydrateGrams, and fatGrams. Estimate grams for counted portions when needed.`
|
|
2429
|
+
});
|
|
2430
|
+
const completion = parsedMealNutritionCompletionSchema.parse(JSON.parse(extractJsonObject(result.outputText)));
|
|
2431
|
+
const completedByIndex = new Map(completion.items.map((item) => [item.index, item]));
|
|
2432
|
+
return {
|
|
2433
|
+
items: items.map((item, index) => {
|
|
2434
|
+
const completed = completedByIndex.get(index);
|
|
2435
|
+
if (!completed) {
|
|
2436
|
+
return item;
|
|
2437
|
+
}
|
|
2438
|
+
const reason = completed.reason || "ChatGPT supplied conservative nutrition values.";
|
|
2439
|
+
return {
|
|
2440
|
+
...item,
|
|
2441
|
+
grams: item.grams ?? completed.grams,
|
|
2442
|
+
calories: item.calories ?? completed.calories,
|
|
2443
|
+
proteinGrams: item.proteinGrams ?? completed.proteinGrams,
|
|
2444
|
+
carbohydrateGrams: item.carbohydrateGrams ?? completed.carbohydrateGrams,
|
|
2445
|
+
fatGrams: item.fatGrams ?? completed.fatGrams,
|
|
2446
|
+
fiberGrams: item.fiberGrams ?? completed.fiberGrams,
|
|
2447
|
+
sugarGrams: item.sugarGrams ?? completed.sugarGrams,
|
|
2448
|
+
sodiumMg: item.sodiumMg ?? completed.sodiumMg,
|
|
2449
|
+
confidence: Math.max(item.confidence, completed.confidence),
|
|
2450
|
+
tags: Array.from(new Set([...item.tags, "chatgpt_validated"])),
|
|
2451
|
+
nutrients: {
|
|
2452
|
+
...item.nutrients,
|
|
2453
|
+
chatGptNutritionValidation: {
|
|
2454
|
+
reason,
|
|
2455
|
+
confidence: completed.confidence
|
|
2456
|
+
}
|
|
2457
|
+
},
|
|
2458
|
+
resolutionNote: item.resolutionNote
|
|
2459
|
+
? `${item.resolutionNote} ${reason}`
|
|
2460
|
+
: reason
|
|
2461
|
+
};
|
|
2462
|
+
}),
|
|
2463
|
+
validationNotes: completion.validationNotes,
|
|
2464
|
+
llmCallCount: 1
|
|
2465
|
+
};
|
|
2466
|
+
}
|
|
2193
2467
|
export async function parseNutritionFoodLogWithChatGpt(input, llm) {
|
|
2468
|
+
const parseStartedAt = Date.now();
|
|
2194
2469
|
const parsed = nutritionParseRequestSchema.parse(input);
|
|
2195
2470
|
const profile = getNutritionCodexProfile(parsed.connectionId);
|
|
2196
2471
|
if (!profile) {
|
|
@@ -2218,7 +2493,9 @@ Return this JSON shape:
|
|
|
2218
2493
|
"loggedAt": "ISO time if known",
|
|
2219
2494
|
"items": [
|
|
2220
2495
|
{
|
|
2221
|
-
"name": "food name",
|
|
2496
|
+
"name": "normalized food name",
|
|
2497
|
+
"searchQuery": "best search query for Forge nutrition catalogs, including corrected brand/product wording when obvious",
|
|
2498
|
+
"brand": "brand or product line if known",
|
|
2222
2499
|
"quantity": 1,
|
|
2223
2500
|
"unit": "serving|g|ml|piece|cup|tbsp|custom",
|
|
2224
2501
|
"grams": null,
|
|
@@ -2238,31 +2515,60 @@ Return this JSON shape:
|
|
|
2238
2515
|
"tags": []
|
|
2239
2516
|
}
|
|
2240
2517
|
|
|
2241
|
-
|
|
2518
|
+
Decompose mixed meals into separate food items. Correct obvious food-brand typos in searchQuery but keep the name human-readable. When a quantity is known from the text, preserve it. For counted foods such as nuts, eggs, fruit, slices, pieces, and bars, estimate grams when possible because Forge scales catalog nutrition by grams. For packaged foods, include brand/product hints in searchQuery. Fill calories, proteinGrams, carbohydrateGrams, and fatGrams with conservative estimates when catalog lookup may fail; use null only when there is genuinely no defensible estimate. Before returning JSON, validate every item against the original text and make sure each item has either a useful searchQuery for public nutrition catalogs or complete calories/proteinGrams/carbohydrateGrams/fatGrams. Mark uncertainty.`;
|
|
2242
2519
|
const result = await llm.runTextPrompt(profile, {
|
|
2243
2520
|
systemPrompt: "You are Forge's nutrition parser. Return strict JSON only. Never claim precision when food quantity is unclear.",
|
|
2244
2521
|
prompt
|
|
2245
2522
|
});
|
|
2246
2523
|
const parsedResult = parsedMealSchema.parse(JSON.parse(extractJsonObject(result.outputText)));
|
|
2524
|
+
const initiallyResolvedItems = await Promise.all(parsedResult.items.map((item) => resolveParsedMealItemNutrition(item)));
|
|
2525
|
+
const { items: resolvedItems, validationNotes, llmCallCount: nutritionCompletionLlmCallCount } = await completeMissingParsedMealNutritionWithChatGpt(parsed.text, initiallyResolvedItems, llm, profile);
|
|
2526
|
+
const stillIncompleteItems = resolvedItems
|
|
2527
|
+
.map((item) => ({
|
|
2528
|
+
name: item.name,
|
|
2529
|
+
missing: missingRequiredNutritionFields(item)
|
|
2530
|
+
}))
|
|
2531
|
+
.filter((item) => item.missing.length > 0);
|
|
2532
|
+
if (stillIncompleteItems.length > 0) {
|
|
2533
|
+
throw new Error(`ChatGPT could not verify complete nutrition for: ${stillIncompleteItems
|
|
2534
|
+
.map((item) => `${item.name} (${item.missing.join(", ")})`)
|
|
2535
|
+
.join("; ")}. Search the catalog or create a custom food with calories, protein, carbs, and fat before logging.`);
|
|
2536
|
+
}
|
|
2537
|
+
const resolutionNotes = resolvedItems
|
|
2538
|
+
.map((item) => item.resolutionNote)
|
|
2539
|
+
.filter((note) => Boolean(note));
|
|
2540
|
+
const parseSummary = {
|
|
2541
|
+
itemCount: resolvedItems.length,
|
|
2542
|
+
completeNutritionItemCount: resolvedItems.filter((item) => missingRequiredNutritionFields(item).length === 0).length,
|
|
2543
|
+
catalogResolvedItemCount: resolvedItems.filter((item) => item.tags.includes("catalog_resolved")).length,
|
|
2544
|
+
chatGptEstimatedItemCount: resolvedItems.filter((item) => item.tags.includes("chatgpt_estimate")).length,
|
|
2545
|
+
chatGptValidatedItemCount: resolvedItems.filter((item) => item.tags.includes("chatgpt_validated")).length,
|
|
2546
|
+
elapsedMs: Date.now() - parseStartedAt,
|
|
2547
|
+
llmCallCount: 1 + nutritionCompletionLlmCallCount
|
|
2548
|
+
};
|
|
2247
2549
|
const candidate = {
|
|
2248
2550
|
userId,
|
|
2249
2551
|
loggedAt: parsedResult.loggedAt ?? parsed.mealTime ?? nowIso(),
|
|
2250
2552
|
mealLabel: parsedResult.mealLabel || "ChatGPT parsed meal",
|
|
2251
2553
|
source: parsed.imageRefs.length > 0 ? "photo" : "chatgpt",
|
|
2252
2554
|
confirmationState: "candidate",
|
|
2253
|
-
notes:
|
|
2555
|
+
notes: [
|
|
2556
|
+
...resolutionNotes,
|
|
2557
|
+
...validationNotes,
|
|
2558
|
+
...parsedResult.uncertaintyReasons
|
|
2559
|
+
].join("; "),
|
|
2254
2560
|
imageRefs: parsed.imageRefs,
|
|
2255
2561
|
parserProvenance: {
|
|
2256
2562
|
provider: "openai-codex",
|
|
2257
2563
|
model: profile.model,
|
|
2258
2564
|
uncertaintyReasons: parsedResult.uncertaintyReasons,
|
|
2259
2565
|
clarificationQuestions: parsedResult.clarificationQuestions,
|
|
2566
|
+
parseSummary,
|
|
2260
2567
|
rawText: parsed.text
|
|
2261
2568
|
},
|
|
2262
2569
|
links: [],
|
|
2263
|
-
items:
|
|
2570
|
+
items: resolvedItems.map(({ resolutionNote: _resolutionNote, ...item }) => ({
|
|
2264
2571
|
...item,
|
|
2265
|
-
nutrients: {},
|
|
2266
2572
|
tags: Array.from(new Set([...item.tags, ...parsedResult.tags]))
|
|
2267
2573
|
}))
|
|
2268
2574
|
};
|
|
@@ -2270,6 +2576,7 @@ Use null for unknown nutrients. Prefer conservative estimates and mark uncertain
|
|
|
2270
2576
|
return {
|
|
2271
2577
|
candidate,
|
|
2272
2578
|
log,
|
|
2579
|
+
parseSummary,
|
|
2273
2580
|
clarificationQuestions: parsedResult.clarificationQuestions,
|
|
2274
2581
|
uncertaintyReasons: parsedResult.uncertaintyReasons
|
|
2275
2582
|
};
|
|
@@ -521,7 +521,7 @@ export class OpenAiResponsesProvider {
|
|
|
521
521
|
? "Connection test."
|
|
522
522
|
: "Reply with the single word ok.",
|
|
523
523
|
...(isCodexProfile(profile) ? { stream: true, store: false } : {}),
|
|
524
|
-
max_output_tokens: 24,
|
|
524
|
+
...(isCodexProfile(profile) ? {} : { max_output_tokens: 24 }),
|
|
525
525
|
reasoning: buildReasoningConfiguration(profile),
|
|
526
526
|
text: buildTextConfiguration({ profile })
|
|
527
527
|
})
|
|
@@ -628,7 +628,7 @@ export class OpenAiResponsesProvider {
|
|
|
628
628
|
...(isCodexProfile(profile) ? { stream: true, store: false } : {}),
|
|
629
629
|
reasoning: buildReasoningConfiguration(profile),
|
|
630
630
|
text: buildTextConfiguration({ profile }),
|
|
631
|
-
max_output_tokens: 1200
|
|
631
|
+
...(isCodexProfile(profile) ? {} : { max_output_tokens: 1200 })
|
|
632
632
|
})
|
|
633
633
|
});
|
|
634
634
|
if (!response.ok) {
|
|
@@ -6152,9 +6152,10 @@ export function buildOpenApiDocument() {
|
|
|
6152
6152
|
additionalProperties: false,
|
|
6153
6153
|
properties: {
|
|
6154
6154
|
text: { type: "string" },
|
|
6155
|
-
|
|
6156
|
-
|
|
6157
|
-
|
|
6155
|
+
mealTime: { type: "string", format: "date-time" },
|
|
6156
|
+
imageRefs: arrayOf({ type: "string" }),
|
|
6157
|
+
connectionId: { type: "string" },
|
|
6158
|
+
commitCandidate: { type: "boolean" }
|
|
6158
6159
|
}
|
|
6159
6160
|
}
|
|
6160
6161
|
}
|
|
@@ -6166,6 +6167,7 @@ export function buildOpenApiDocument() {
|
|
|
6166
6167
|
required: [
|
|
6167
6168
|
"candidate",
|
|
6168
6169
|
"log",
|
|
6170
|
+
"parseSummary",
|
|
6169
6171
|
"clarificationQuestions",
|
|
6170
6172
|
"uncertaintyReasons"
|
|
6171
6173
|
],
|
|
@@ -6176,6 +6178,27 @@ export function buildOpenApiDocument() {
|
|
|
6176
6178
|
log: nullable({
|
|
6177
6179
|
$ref: "#/components/schemas/NutritionFoodLog"
|
|
6178
6180
|
}),
|
|
6181
|
+
parseSummary: {
|
|
6182
|
+
type: "object",
|
|
6183
|
+
required: [
|
|
6184
|
+
"itemCount",
|
|
6185
|
+
"completeNutritionItemCount",
|
|
6186
|
+
"catalogResolvedItemCount",
|
|
6187
|
+
"chatGptEstimatedItemCount",
|
|
6188
|
+
"chatGptValidatedItemCount",
|
|
6189
|
+
"elapsedMs",
|
|
6190
|
+
"llmCallCount"
|
|
6191
|
+
],
|
|
6192
|
+
properties: {
|
|
6193
|
+
itemCount: { type: "number" },
|
|
6194
|
+
completeNutritionItemCount: { type: "number" },
|
|
6195
|
+
catalogResolvedItemCount: { type: "number" },
|
|
6196
|
+
chatGptEstimatedItemCount: { type: "number" },
|
|
6197
|
+
chatGptValidatedItemCount: { type: "number" },
|
|
6198
|
+
elapsedMs: { type: "number" },
|
|
6199
|
+
llmCallCount: { type: "number" }
|
|
6200
|
+
}
|
|
6201
|
+
},
|
|
6179
6202
|
clarificationQuestions: arrayOf({ type: "string" }),
|
|
6180
6203
|
uncertaintyReasons: arrayOf({ type: "string" })
|
|
6181
6204
|
}
|
|
@@ -778,7 +778,7 @@ async function runOpenAiConversationPrompt(input) {
|
|
|
778
778
|
text: typeof input.profile.metadata.verbosity === "string"
|
|
779
779
|
? { verbosity: input.profile.metadata.verbosity }
|
|
780
780
|
: undefined,
|
|
781
|
-
max_output_tokens: 1200
|
|
781
|
+
...(isCodexProfile(input.profile) ? {} : { max_output_tokens: 1200 })
|
|
782
782
|
})
|
|
783
783
|
});
|
|
784
784
|
if (!response.ok) {
|
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.3.
|
|
5
|
+
"version": "0.3.14",
|
|
6
6
|
"activation": {
|
|
7
7
|
"onStartup": true,
|
|
8
8
|
"onCapabilities": [
|
package/package.json
CHANGED
|
@@ -193,6 +193,15 @@ Wiki rule:
|
|
|
193
193
|
- Use the wiki tools when the user wants SQLite-backed reference pages, backlink-aware recall, ingest from a URL or local file, or wiki maintenance work such as unresolved-link cleanup.
|
|
194
194
|
- `forge_ingest_wiki_source` now queues the ingest as background work; use the Forge UI handoff when the user wants to review or keep only selected wiki/entity candidates after the ingest finishes.
|
|
195
195
|
- Keep evidence notes and wiki pages conceptually distinct: evidence notes are linked operating records, while wiki pages are curated long-form memory.
|
|
196
|
+
- Ingestion policy for OpenClaw, Hermes, Codex, and Claude Code adapters:
|
|
197
|
+
- Before ingesting, call `forge_get_wiki_settings` so the agent knows the available spaces, LLM profiles, and embedding profiles. Prefer the shared wiki space for durable shared knowledge unless the current settings or user request clearly require another space.
|
|
198
|
+
- Use `forge_ingest_wiki_source` for raw text, local files, and URLs. Do not build an ad hoc importer or write raw source pages manually unless the Forge ingest tool is broken; if it is broken, fix the ingest path and tests before proceeding.
|
|
199
|
+
- Preserve source/evidence artifacts for audit, but do not make the wiki a transcript dump, movement log, release log, or repetitive check-in archive. Evidence supports knowledge; canonical wiki pages should be curated, readable, structured articles.
|
|
200
|
+
- Detect and propose durable pages for important people, organizations, projects, places, events, concepts, recurring relationship patterns, decisions, preferences, commitments, and timelines. A detected person or important concept should become or update a real wiki page, not disappear into one source note.
|
|
201
|
+
- Merge duplicates deliberately into one canonical page per real concept/person/project. Do not merely rename a page, hide a duplicate, or keep competing partial pages. Combine the durable information, preserve aliases and backlinks, and link the original evidence/source pages from the canonical page.
|
|
202
|
+
- Keep normal personal context when it is useful knowledge. Redact or omit actual secrets and security/payment credentials such as passwords, API keys, tokens, private auth links, card numbers, and similar material; do not over-redact ordinary names, relationships, work context, events, or preferences just because they are personal.
|
|
203
|
+
- After ingest or merge work, run `forge_sync_wiki_vault`, then use `forge_get_wiki_health`, search/list checks, and spot reads to verify created and updated pages, duplicate candidates, unresolved links, missing summaries, evidence links, and whether source material is reachable from canonical pages.
|
|
204
|
+
- Report what was created, updated, merged, left unresolved, and how evidence is preserved. If the user asked for reviewable candidates, hand off to the Forge UI ingest review instead of pretending the adapter can approve candidates inline.
|
|
196
205
|
|
|
197
206
|
Wiki navigation and search rule:
|
|
198
207
|
|
|
@@ -263,6 +272,10 @@ Entity conversation rule:
|
|
|
263
272
|
- Do not ask for optional tags, priority, status, dates, color, links, or assignees
|
|
264
273
|
when accepted wording and meaningful body are enough unless that metadata changes
|
|
265
274
|
accountability, retrieval, or execution.
|
|
275
|
+
- After a read returns data and several next actions are plausible, choose the one
|
|
276
|
+
most directly supported by what was learned and ask only for the missing detail
|
|
277
|
+
that would permit that action. Do not hand the user a broad menu after the read has
|
|
278
|
+
already narrowed the work.
|
|
266
279
|
- Let each question have one job. Know what you are trying to clarify before you ask it.
|
|
267
280
|
- Before you ask, decide the exact missing thing you need and how that answer will help you name, place, or save the record.
|
|
268
281
|
- Prefer a progression of:
|
|
@@ -335,6 +348,9 @@ Psyche interview rule:
|
|
|
335
348
|
- Phrase interpretive hypotheses as collaborative and testable, not as verdicts. A good hypothesis says what the reaction may be protecting, predicting, relieving, or costing, then asks whether that lands or needs correction.
|
|
336
349
|
- For Psyche hypotheses, reduce the formulation burden. After one concrete example, offer one tentative function, danger, protection, payoff, or cost hypothesis and ask one fit-or-correction question. Do not make the user prove the experience, list evidence, or design repair before the wording feels held.
|
|
337
350
|
- 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.
|
|
351
|
+
- Do not leave the user with interpretation alone. Once the hypothesis lands or is
|
|
352
|
+
corrected, name the primary Forge record it becomes and ask one accuracy or consent
|
|
353
|
+
question that moves toward saving the corrected formulation.
|
|
338
354
|
- 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.
|
|
339
355
|
- If several Psyche containers are plausible, do not ask the user to choose from a taxonomy menu first. Reflect the lived difference, offer one careful hypothesis when a concrete example is visible, then distinguish the options in plain language: one episode as a `trigger_report`, a recurring loop as a `behavior_pattern`, one repeated move as `behavior`, one sentence as `belief_entry`, a part-state as `mode_profile` or `mode_guide_session`, or reusable future-labeling as `event_type` or `emotion_definition`.
|
|
340
356
|
- For Psyche updates, start with what feels newly true, newly visible, or newly inaccurate, then ask what should stay true before changing the formulation.
|
|
@@ -660,6 +676,12 @@ through `forge_create_entities` or `forge_update_entities`.
|
|
|
660
676
|
- If you are unsure which specialized route family applies, check `forge_get_agent_onboarding` and use its `entityRouteModel.specializedDomainSurfaces` section before guessing.
|
|
661
677
|
- If the truth of the current Movement, Life Force, or Workbench state is still unclear, prefer the dedicated read before the mutation so the correction stays truthful.
|
|
662
678
|
- After a concrete Movement, Life Force, or Workbench correction, mutation, or result-producing run, read the relevant specialized view back when the user is trying to understand the result rather than only store it: timeline or place/settings detail for Movement, the Life Force overview for energy-planning impact, and flow detail, run detail, node result, latest node output, published output, or run history for Workbench.
|
|
679
|
+
- After any dedicated Movement, Life Force, or Workbench read, translate the result
|
|
680
|
+
into one next action: no change, Movement overlay/place/settings/link, Life Force
|
|
681
|
+
workload/recovery/timebox/meeting/task-choice change, or Workbench
|
|
682
|
+
rerun/node-inspection/flow-edit/publish/preserve/stop. Ask only for the missing
|
|
683
|
+
span, place, weekday, flow, run, node, output, correction, preservation choice, or
|
|
684
|
+
confirmation that would change that action.
|
|
663
685
|
|
|
664
686
|
Use live work tools for `task_run`:
|
|
665
687
|
`forge_log_work`, `forge_start_task_run`, `forge_heartbeat_task_run`, `forge_focus_task_run`, `forge_complete_task_run`, `forge_release_task_run`
|
|
@@ -222,6 +222,10 @@ worked.
|
|
|
222
222
|
- Ask a follow-up only if it changes the next action: save, update, correct, link,
|
|
223
223
|
schedule, run, publish, enrich, or open the UI. If the read already answers the
|
|
224
224
|
question, close cleanly instead of asking a ceremonial "what next?"
|
|
225
|
+
- If the read produces several possible actions, choose the one that most directly
|
|
226
|
+
answers the user's practical question and ask only for the missing detail that would
|
|
227
|
+
permit that action. Do not hand the user a broad menu after you just learned enough
|
|
228
|
+
to narrow the next move.
|
|
225
229
|
- For Movement, Life Force, Workbench, calendar, health, and operator overviews,
|
|
226
230
|
keep the follow-up anchored to the read result: the span that is missing, the
|
|
227
231
|
weekday curve that needs correction, the failed run or node, the overloaded day, or
|
|
@@ -2023,6 +2027,10 @@ Direct action rules:
|
|
|
2023
2027
|
object.
|
|
2024
2028
|
- If the user is asking where they were during one uncertain window, prefer a timeline
|
|
2025
2029
|
read before you create a correction. Mutate only after the lived truth is clear.
|
|
2030
|
+
- After a Movement read, translate the returned data into one next action: no change,
|
|
2031
|
+
a manual overlay, a place boundary correction, a settings change, or a linked note.
|
|
2032
|
+
Ask only for the missing span, place, boundary, or confirmation that enables that
|
|
2033
|
+
action.
|
|
2026
2034
|
- When the user has already given the real answer, for example "I stayed home during
|
|
2027
2035
|
that missing block", do not ask a broad review question again. Confirm only the
|
|
2028
2036
|
interval or place if that is still ambiguous, then act.
|
|
@@ -2167,6 +2175,10 @@ Direct action rules:
|
|
|
2167
2175
|
- After a fatigue signal, profile patch, or weekday-template edit, verify through the
|
|
2168
2176
|
Life Force overview when the next planning decision depends on the updated energy
|
|
2169
2177
|
picture.
|
|
2178
|
+
- After a Life Force overview, translate the read into one planning implication before
|
|
2179
|
+
asking for a write: lighter workload, added recovery, protected timebox, meeting
|
|
2180
|
+
change, task-choice change, or no change. Ask for a profile, template, or signal
|
|
2181
|
+
detail only when that implication requires a mutation.
|
|
2170
2182
|
|
|
2171
2183
|
Ready to act when:
|
|
2172
2184
|
|
|
@@ -2273,6 +2285,11 @@ Direct action rules:
|
|
|
2273
2285
|
- If the user wants to understand what inputs a flow can accept before editing or
|
|
2274
2286
|
running it, read the box catalog or flow detail before asking for structured
|
|
2275
2287
|
input details.
|
|
2288
|
+
- After a Workbench read, translate the returned artifact into one next action:
|
|
2289
|
+
rerun with clearer input, inspect a specific node, edit the saved flow, publish or
|
|
2290
|
+
preserve the output, or stop because the answer is already sufficient. Ask only for
|
|
2291
|
+
the missing input, node, run, preservation choice, or confirmation that would change
|
|
2292
|
+
that action.
|
|
2276
2293
|
- For new flows, ask what the flow should reliably produce, what input contract it
|
|
2277
2294
|
should accept, and what first node or box should anchor it. Do not start by asking
|
|
2278
2295
|
for raw JSON.
|
|
@@ -297,6 +297,9 @@ of leaving it as warm reflective prose.
|
|
|
297
297
|
that.
|
|
298
298
|
- Save through shared batch entity routes only after the user accepts the working
|
|
299
299
|
wording or explicitly asks to save.
|
|
300
|
+
- Do not leave the user with interpretation alone. Once the hypothesis lands or is
|
|
301
|
+
corrected, name the primary Forge record it becomes and ask one accuracy or consent
|
|
302
|
+
question that moves toward that save.
|
|
300
303
|
|
|
301
304
|
## Psyche progressive disclosure
|
|
302
305
|
|
|
@@ -317,6 +320,10 @@ should preserve momentum instead of making them retell the whole story.
|
|
|
317
320
|
unclear.
|
|
318
321
|
- If the working material is already accurate enough, ask one accuracy or consent
|
|
319
322
|
question instead of reopening origin, evidence, or repair.
|
|
323
|
+
- If the user accepts or corrects a hypothesis, immediately turn the corrected
|
|
324
|
+
interpretation into the nearest saveable shape: belief sentence, functional loop,
|
|
325
|
+
part voice, trigger chain, value phrase, event kind, emotion signature, or
|
|
326
|
+
flashcard message.
|
|
320
327
|
- If the remaining unknown is optional therapeutic metadata, save a provisional
|
|
321
328
|
version after one accuracy check and preserve nuance in a linked note when needed.
|
|
322
329
|
|