forge-openclaw-plugin 0.2.45 → 0.2.48
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/README.md +6 -5
- package/dist/assets/{index-BejDHw1R.js → index-Bv9FWWsZ.js} +44 -44
- package/dist/assets/index-Bv9FWWsZ.js.map +1 -0
- package/dist/index.html +1 -1
- package/dist/openclaw/api-client.js +15 -1
- package/dist/openclaw/tools.js +1 -1
- package/dist/server/server/src/app.js +20 -11
- package/dist/server/server/src/health-workout-adapters.js +465 -0
- package/dist/server/server/src/health.js +134 -9
- package/dist/server/server/src/openapi.js +14 -0
- package/dist/server/server/src/repositories/habits.js +62 -25
- package/dist/server/server/src/types.js +9 -6
- package/dist/server/server/src/watch-mobile.js +33 -21
- package/dist/server/src/lib/date-keys.js +21 -0
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/skills/forge-openclaw/SKILL.md +3 -1
- package/skills/forge-openclaw/entity_conversation_playbooks.md +26 -8
- package/skills/forge-openclaw/psyche_entity_playbooks.md +3 -0
- package/dist/assets/index-BejDHw1R.js.map +0 -1
|
@@ -5,6 +5,7 @@ import { HttpError } from "./errors.js";
|
|
|
5
5
|
import { updateWorkoutMetadata } from "./health.js";
|
|
6
6
|
import { canonicalizeMovementCategoryTags, listMovementPlaces, normalizeMovementCategoryTag, updateMovementPlace } from "./movement.js";
|
|
7
7
|
import { listHabits } from "./repositories/habits.js";
|
|
8
|
+
import { formatLocalDateKey } from "../../src/lib/date-keys.js";
|
|
8
9
|
const watchCapability = "watch-ready";
|
|
9
10
|
const watchHistoryStateSchema = z.enum(["aligned", "unaligned", "unknown"]);
|
|
10
11
|
const watchPromptKindSchema = z.enum([
|
|
@@ -56,7 +57,11 @@ export const mobileWatchHabitCheckInSchema = z.object({
|
|
|
56
57
|
sessionId: z.string().trim().min(1),
|
|
57
58
|
pairingToken: z.string().trim().min(1),
|
|
58
59
|
dedupeKey: z.string().trim().min(1),
|
|
59
|
-
dateKey: z
|
|
60
|
+
dateKey: z
|
|
61
|
+
.string()
|
|
62
|
+
.trim()
|
|
63
|
+
.min(1)
|
|
64
|
+
.default(() => formatLocalDateKey()),
|
|
60
65
|
status: z.enum(["done", "missed"]),
|
|
61
66
|
note: z.string().trim().default(""),
|
|
62
67
|
description: z.string().trim().optional()
|
|
@@ -82,24 +87,24 @@ function nowIso() {
|
|
|
82
87
|
return new Date().toISOString();
|
|
83
88
|
}
|
|
84
89
|
function formatDateKey(date) {
|
|
85
|
-
return date
|
|
90
|
+
return formatLocalDateKey(date);
|
|
86
91
|
}
|
|
87
92
|
function parseDateKey(dateKey) {
|
|
88
93
|
const [year, month, day] = dateKey.split("-").map(Number);
|
|
89
|
-
return new Date(
|
|
94
|
+
return new Date(year, month - 1, day);
|
|
90
95
|
}
|
|
91
|
-
function
|
|
92
|
-
return new Date(
|
|
96
|
+
function startOfLocalDay(date) {
|
|
97
|
+
return new Date(date.getFullYear(), date.getMonth(), date.getDate());
|
|
93
98
|
}
|
|
94
|
-
function
|
|
99
|
+
function addLocalDays(date, days) {
|
|
95
100
|
const next = new Date(date);
|
|
96
|
-
next.
|
|
101
|
+
next.setDate(next.getDate() + days);
|
|
97
102
|
return next;
|
|
98
103
|
}
|
|
99
|
-
function
|
|
100
|
-
const start =
|
|
101
|
-
const offset = (start.
|
|
102
|
-
start.
|
|
104
|
+
function startOfLocalWeek(date) {
|
|
105
|
+
const start = startOfLocalDay(date);
|
|
106
|
+
const offset = (start.getDay() + 6) % 7;
|
|
107
|
+
start.setDate(start.getDate() - offset);
|
|
103
108
|
return start;
|
|
104
109
|
}
|
|
105
110
|
function isAlignedCheckIn(habit, status) {
|
|
@@ -125,15 +130,15 @@ function buildHabitHistory(habit, options) {
|
|
|
125
130
|
? parseDateKey(options.anchorDateKey)
|
|
126
131
|
: new Date();
|
|
127
132
|
if (habit.frequency === "daily") {
|
|
128
|
-
const today =
|
|
133
|
+
const today = startOfLocalDay(now);
|
|
129
134
|
return Array.from({ length: 7 }, (_, index) => {
|
|
130
135
|
const offset = index - 6;
|
|
131
|
-
const date =
|
|
136
|
+
const date = addLocalDays(today, offset);
|
|
132
137
|
const dateKey = formatDateKey(date);
|
|
133
138
|
const checkIn = habit.checkIns.find((entry) => entry.dateKey === dateKey) ?? null;
|
|
134
139
|
return {
|
|
135
140
|
id: dateKey,
|
|
136
|
-
label: ["S", "M", "T", "W", "T", "F", "S"][date.
|
|
141
|
+
label: ["S", "M", "T", "W", "T", "F", "S"][date.getDay()],
|
|
137
142
|
periodKey: dateKey,
|
|
138
143
|
current: offset === 0,
|
|
139
144
|
state: checkIn
|
|
@@ -144,13 +149,13 @@ function buildHabitHistory(habit, options) {
|
|
|
144
149
|
};
|
|
145
150
|
});
|
|
146
151
|
}
|
|
147
|
-
const thisWeek =
|
|
152
|
+
const thisWeek = startOfLocalWeek(now);
|
|
148
153
|
return Array.from({ length: 7 }, (_, index) => {
|
|
149
154
|
const offset = index - 6;
|
|
150
|
-
const weekStart =
|
|
155
|
+
const weekStart = addLocalDays(thisWeek, offset * 7);
|
|
151
156
|
const weekKey = formatDateKey(weekStart);
|
|
152
157
|
const weekEntries = habit.checkIns.filter((entry) => {
|
|
153
|
-
const entryWeek = formatDateKey(
|
|
158
|
+
const entryWeek = formatDateKey(startOfLocalWeek(parseDateKey(entry.dateKey)));
|
|
154
159
|
return entryWeek === weekKey;
|
|
155
160
|
});
|
|
156
161
|
const alignedCount = weekEntries.filter((entry) => isAlignedCheckIn(habit, entry.status)).length;
|
|
@@ -375,7 +380,9 @@ function projectionForStoredEvent(event) {
|
|
|
375
380
|
const categoryCandidate = Array.isArray(event.payload.categoryTags)
|
|
376
381
|
? event.payload.categoryTags
|
|
377
382
|
: typeof event.payload.category === "string"
|
|
378
|
-
? watchCategoryMap.get(event.payload.category) ?? [
|
|
383
|
+
? (watchCategoryMap.get(event.payload.category) ?? [
|
|
384
|
+
event.payload.category
|
|
385
|
+
])
|
|
379
386
|
: [];
|
|
380
387
|
const categoryTags = canonicalizeMovementCategoryTags(categoryCandidate.flatMap((value) => typeof value === "string" ? [normalizeMovementCategoryTag(value)] : []));
|
|
381
388
|
try {
|
|
@@ -403,12 +410,15 @@ function projectionForStoredEvent(event) {
|
|
|
403
410
|
status: "projection_failed",
|
|
404
411
|
details: {
|
|
405
412
|
reason: "place_update_failed",
|
|
406
|
-
message: error instanceof Error
|
|
413
|
+
message: error instanceof Error
|
|
414
|
+
? error.message
|
|
415
|
+
: "Unknown place update error"
|
|
407
416
|
}
|
|
408
417
|
};
|
|
409
418
|
}
|
|
410
419
|
}
|
|
411
|
-
if (event.eventType === "workout_annotation" &&
|
|
420
|
+
if (event.eventType === "workout_annotation" &&
|
|
421
|
+
event.linkedContext.workoutId) {
|
|
412
422
|
try {
|
|
413
423
|
const workout = updateWorkoutMetadata(event.linkedContext.workoutId, {
|
|
414
424
|
subjectiveEffort: typeof event.payload.subjectiveEffort === "number"
|
|
@@ -449,7 +459,9 @@ function projectionForStoredEvent(event) {
|
|
|
449
459
|
status: "projection_failed",
|
|
450
460
|
details: {
|
|
451
461
|
reason: "workout_update_failed",
|
|
452
|
-
message: error instanceof Error
|
|
462
|
+
message: error instanceof Error
|
|
463
|
+
? error.message
|
|
464
|
+
: "Unknown workout update error"
|
|
453
465
|
}
|
|
454
466
|
};
|
|
455
467
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
function readPart(parts, type) {
|
|
2
|
+
return parts.find((part) => part.type === type)?.value ?? "";
|
|
3
|
+
}
|
|
4
|
+
export function getRuntimeTimeZone() {
|
|
5
|
+
return Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC";
|
|
6
|
+
}
|
|
7
|
+
export function formatDateKeyInTimeZone(date, timeZone) {
|
|
8
|
+
const parts = new Intl.DateTimeFormat("en-CA", {
|
|
9
|
+
timeZone,
|
|
10
|
+
year: "numeric",
|
|
11
|
+
month: "2-digit",
|
|
12
|
+
day: "2-digit"
|
|
13
|
+
}).formatToParts(date);
|
|
14
|
+
const year = readPart(parts, "year");
|
|
15
|
+
const month = readPart(parts, "month");
|
|
16
|
+
const day = readPart(parts, "day");
|
|
17
|
+
return `${year}-${month}-${day}`;
|
|
18
|
+
}
|
|
19
|
+
export function formatLocalDateKey(date = new Date()) {
|
|
20
|
+
return formatDateKeyInTimeZone(date, getRuntimeTimeZone());
|
|
21
|
+
}
|
package/openclaw.plugin.json
CHANGED
package/package.json
CHANGED
|
@@ -239,7 +239,8 @@ CRITICAL NEGATIVE-HABIT CHECK-IN RULE:
|
|
|
239
239
|
- For a `negative` habit, the correct check-in outcome is `missed`.
|
|
240
240
|
- On a `negative` habit, `missed` means the habit was resisted, the user stayed aligned, and the habit earns its XP bonus.
|
|
241
241
|
- Do not treat `missed` on a `negative` habit as failure. In this case, `missed` is the successful outcome.
|
|
242
|
-
-
|
|
242
|
+
- In OpenClaw, official habit outcome logging should go through `forge_update_entities` on `entityType: "habit"` with `patch: { checkIn: { status, dateKey?, note?, description? } }`.
|
|
243
|
+
- Do not bypass the shared tool model with raw habit routes when the batch entity update already covers the write cleanly.
|
|
243
244
|
Ask:
|
|
244
245
|
|
|
245
246
|
1. What is the recurring behavior in one concrete sentence?
|
|
@@ -387,6 +388,7 @@ Use the dedicated domain routes for specialized surfaces that are not simple bat
|
|
|
387
388
|
`/api/v1/workbench/flows/:id/output` for published outputs, and the run/node routes
|
|
388
389
|
under `/api/v1/workbench/flows/:id` for run history and node-level inspection.
|
|
389
390
|
- If you are unsure which specialized route family applies, check `forge_get_agent_onboarding` and use its `entityRouteModel.specializedDomainSurfaces` section before guessing.
|
|
391
|
+
- 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.
|
|
390
392
|
- After a concrete Movement, Life Force, or Workbench correction, read the relevant specialized view back when the user is trying to understand the result rather than only store it.
|
|
391
393
|
|
|
392
394
|
Use live work tools for `task_run`:
|
|
@@ -56,6 +56,8 @@ Forge correctly, and gather only the structure that still matters.
|
|
|
56
56
|
- For specialized surfaces, start from the user's real job in plain language, then
|
|
57
57
|
narrow to the route family. Do not open with a route menu unless the user already
|
|
58
58
|
named the exact object and action.
|
|
59
|
+
- For specialized surfaces, if the truth of the current state is still uncertain, read
|
|
60
|
+
the relevant dedicated view before you mutate it.
|
|
59
61
|
- When the user has already named a precise correction or review target, do not widen
|
|
60
62
|
back out into a meta lane question. Confirm only the missing route-selecting detail
|
|
61
63
|
and then act.
|
|
@@ -411,7 +413,7 @@ about linking only after the main record already feels named and steady.
|
|
|
411
413
|
|
|
412
414
|
Use this when the user is updating an existing record rather than creating a new one.
|
|
413
415
|
|
|
414
|
-
1. Ask what feels newly true, newly
|
|
416
|
+
1. Ask what feels newly true, newly inaccurate, or newly clear.
|
|
415
417
|
2. Ask what should stay true so the record keeps its core meaning.
|
|
416
418
|
3. Ask what prompted the update now if that changes the shape of the record.
|
|
417
419
|
4. Then ask only for the missing structural detail required by the change.
|
|
@@ -1017,9 +1019,11 @@ Arc:
|
|
|
1017
1019
|
5. Ask what they are trying to notice, preserve, or answer through that movement context.
|
|
1018
1020
|
6. Choose the dedicated day, month, all-time, timeline, places, trip-detail, or
|
|
1019
1021
|
selection route once the question shape is clear.
|
|
1020
|
-
7.
|
|
1022
|
+
7. If the truth of one uncertain span is still unclear, read the timeline or saved-box
|
|
1023
|
+
detail before you mutate it.
|
|
1024
|
+
8. Skip the meta lane question when the user already named the exact correction or
|
|
1021
1025
|
review target and only one ambiguity remains.
|
|
1022
|
-
|
|
1026
|
+
9. Route to the dedicated movement read or write path once the surface is clear.
|
|
1023
1027
|
|
|
1024
1028
|
Direct action rules:
|
|
1025
1029
|
|
|
@@ -1036,6 +1040,8 @@ Direct action rules:
|
|
|
1036
1040
|
- If the user wants to inspect one already-saved movement correction before editing
|
|
1037
1041
|
it, read the box detail first so the follow-up write stays grounded in the saved
|
|
1038
1042
|
object.
|
|
1043
|
+
- If the user is asking where they were during one uncertain window, prefer a timeline
|
|
1044
|
+
read before you create a correction. Mutate only after the lived truth is clear.
|
|
1039
1045
|
- When the user has already given the real answer, for example "I stayed home during
|
|
1040
1046
|
that missing block", do not ask a broad review question again. Confirm only the
|
|
1041
1047
|
interval or place if that is still ambiguous, then act.
|
|
@@ -1105,11 +1111,14 @@ Arc:
|
|
|
1105
1111
|
4. Ask what should stay true if they are changing profile or template assumptions.
|
|
1106
1112
|
5. Ask whether the user is describing a stable weekly shape or just how today feels
|
|
1107
1113
|
when the lane is still blurred.
|
|
1108
|
-
6. If the user
|
|
1114
|
+
6. If the user describes a repeatable day-shape such as "Mondays crash after lunch",
|
|
1115
|
+
treat that as a weekday-template question before you reach for profile or
|
|
1116
|
+
fatigue-signal routes.
|
|
1117
|
+
7. If the user already named the life-force lane clearly, skip the meta lane question
|
|
1109
1118
|
and ask only for the specific weekday, profile field, or signal that still matters.
|
|
1110
|
-
|
|
1119
|
+
8. If the user wants to see what changed after a write, read the overview back instead
|
|
1111
1120
|
of leaving the result implicit.
|
|
1112
|
-
|
|
1121
|
+
9. Route to the dedicated life-force path once the lane is clear.
|
|
1113
1122
|
|
|
1114
1123
|
Helpful follow-up lanes:
|
|
1115
1124
|
|
|
@@ -1133,6 +1142,8 @@ Direct action rules:
|
|
|
1133
1142
|
|
|
1134
1143
|
- If the user is describing a durable baseline such as work capacity, recovery style,
|
|
1135
1144
|
or action-point assumptions, patch the profile instead of logging a fatigue signal.
|
|
1145
|
+
- If the user is describing a repeatable weekday rhythm, update that weekday template
|
|
1146
|
+
instead of treating it as a one-off right-now feeling.
|
|
1136
1147
|
- If the user is describing how one weekday should usually feel, update that weekday
|
|
1137
1148
|
template instead of editing the profile.
|
|
1138
1149
|
- If the user is describing right-now depletion or recovery, post a fatigue signal and
|
|
@@ -1161,12 +1172,16 @@ Arc:
|
|
|
1161
1172
|
before you narrow to flow discovery, editing, execution, or results.
|
|
1162
1173
|
2. Ask whether the job is flow discovery, one flow edit, execution, run history, published output, node-level inspection, or latest-node-output lookup.
|
|
1163
1174
|
3. Ask which flow, slug, run, or node the request is about.
|
|
1164
|
-
4. Ask whether they need the flow contract,
|
|
1175
|
+
4. Ask whether they need the stable flow contract, one run result, one published
|
|
1176
|
+
output, one node result, or the latest node output.
|
|
1165
1177
|
5. If the user already named the flow and action clearly, skip the meta lane
|
|
1166
1178
|
question and ask only for the missing run, node, or output scope.
|
|
1167
1179
|
6. If the user wants a stable public input contract or published output, prefer those
|
|
1168
1180
|
dedicated reads instead of detouring through run history first.
|
|
1169
|
-
7.
|
|
1181
|
+
7. If the user is debugging one failed run, ask whether the useful artifact is the run
|
|
1182
|
+
summary, one node result, the latest node output, or the published output before
|
|
1183
|
+
you start asking for edits.
|
|
1184
|
+
8. Route to the dedicated workbench route family once the execution lane is clear.
|
|
1170
1185
|
|
|
1171
1186
|
Helpful follow-up lanes:
|
|
1172
1187
|
|
|
@@ -1205,6 +1220,9 @@ Direct action rules:
|
|
|
1205
1220
|
- If the user wants to execute a known saved flow, use `/api/v1/workbench/flows/:id/run`.
|
|
1206
1221
|
- If the user wants payload-first execution without depending on a saved flow id, use
|
|
1207
1222
|
`/api/v1/workbench/run`.
|
|
1223
|
+
- If the user wants to debug one failed execution, narrow whether they need the run
|
|
1224
|
+
detail, one node result, the latest node output, or the published output before you
|
|
1225
|
+
ask for flow changes.
|
|
1208
1226
|
- If the user wants one node's latest successful output, do not browse old runs first
|
|
1209
1227
|
unless they explicitly want historical debugging.
|
|
1210
1228
|
- If the user wants to understand what inputs a flow can accept before editing or
|
|
@@ -383,6 +383,9 @@ If the entity is not yet clear:
|
|
|
383
383
|
hold and what the new episode or evidence changes.
|
|
384
384
|
- If a recent charged episode is what made the update visible, anchor in that episode
|
|
385
385
|
before you rename the durable belief, pattern, mode, or value.
|
|
386
|
+
- If the user already gives the new sentence in usable language, revise the wording
|
|
387
|
+
once and save instead of reopening evidence, origin, or repair just because those
|
|
388
|
+
lanes are available.
|
|
386
389
|
- If another belief, value, pattern, mode, or note becomes visible, name it gently but
|
|
387
390
|
do not switch containers unless the user wants to.
|
|
388
391
|
|