forge-openclaw-plugin 0.2.67 → 0.2.69
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/{board-DFNV9VAZ.js → board-BfqxFNiQ.js} +1 -1
- package/dist/assets/index-BfLQnCNZ.js +91 -0
- package/dist/assets/index-DIapFz9v.css +1 -0
- package/dist/assets/{motion-CXdn34ih.js → motion-C0ALlgho.js} +1 -1
- package/dist/assets/{table-CEq3bTDv.js → table-WcMjnJll.js} +1 -1
- package/dist/assets/{ui-g7FaEglG.js → ui-B5I-3U91.js} +1 -1
- package/dist/assets/vendor-B-Lq_OG3.css +1 -0
- package/dist/assets/vendor-C56o26_3.js +2163 -0
- package/dist/index.html +8 -8
- package/dist/server/server/migrations/060_psyche_devrage_metrics.sql +40 -0
- package/dist/server/server/migrations/061_health_workout_raw_evidence.sql +95 -0
- package/dist/server/server/src/app.js +94 -12
- package/dist/server/server/src/health-workout-analytics.js +572 -0
- package/dist/server/server/src/health.js +116 -3
- package/dist/server/server/src/openapi.js +238 -0
- package/dist/server/server/src/psyche-types.js +90 -0
- package/dist/server/server/src/services/devrage.js +412 -0
- package/dist/server/server/src/services/psyche.js +3 -0
- package/dist/server/src/lib/api.js +13 -0
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/server/migrations/060_psyche_devrage_metrics.sql +40 -0
- package/server/migrations/061_health_workout_raw_evidence.sql +95 -0
- package/skills/forge-openclaw/SKILL.md +25 -1
- package/skills/forge-openclaw/entity_conversation_playbooks.md +46 -14
- package/dist/assets/index-B9IVt8VN.js +0 -90
- package/dist/assets/index-DJlo9Tsp.css +0 -1
- package/dist/assets/vendor-BcOHGipZ.js +0 -1341
- package/dist/assets/vendor-DT3pnAKJ.css +0 -1
package/dist/index.html
CHANGED
|
@@ -13,14 +13,14 @@
|
|
|
13
13
|
/>
|
|
14
14
|
<link rel="icon" type="image/png" href="/forge/assets/favicon-BCHm9dUV.ico" />
|
|
15
15
|
<link rel="alternate icon" href="/forge/assets/favicon-BCHm9dUV.ico" />
|
|
16
|
-
<script type="module" crossorigin src="/forge/assets/index-
|
|
17
|
-
<link rel="modulepreload" crossorigin href="/forge/assets/vendor-
|
|
18
|
-
<link rel="modulepreload" crossorigin href="/forge/assets/board-
|
|
19
|
-
<link rel="modulepreload" crossorigin href="/forge/assets/ui-
|
|
20
|
-
<link rel="modulepreload" crossorigin href="/forge/assets/motion-
|
|
21
|
-
<link rel="modulepreload" crossorigin href="/forge/assets/table-
|
|
22
|
-
<link rel="stylesheet" crossorigin href="/forge/assets/vendor-
|
|
23
|
-
<link rel="stylesheet" crossorigin href="/forge/assets/index-
|
|
16
|
+
<script type="module" crossorigin src="/forge/assets/index-BfLQnCNZ.js"></script>
|
|
17
|
+
<link rel="modulepreload" crossorigin href="/forge/assets/vendor-C56o26_3.js">
|
|
18
|
+
<link rel="modulepreload" crossorigin href="/forge/assets/board-BfqxFNiQ.js">
|
|
19
|
+
<link rel="modulepreload" crossorigin href="/forge/assets/ui-B5I-3U91.js">
|
|
20
|
+
<link rel="modulepreload" crossorigin href="/forge/assets/motion-C0ALlgho.js">
|
|
21
|
+
<link rel="modulepreload" crossorigin href="/forge/assets/table-WcMjnJll.js">
|
|
22
|
+
<link rel="stylesheet" crossorigin href="/forge/assets/vendor-B-Lq_OG3.css">
|
|
23
|
+
<link rel="stylesheet" crossorigin href="/forge/assets/index-DIapFz9v.css">
|
|
24
24
|
</head>
|
|
25
25
|
<body class="bg-canvas text-ink antialiased">
|
|
26
26
|
<div id="root"></div>
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
CREATE TABLE IF NOT EXISTS psyche_devrage_conversation_measures (
|
|
2
|
+
id TEXT PRIMARY KEY,
|
|
3
|
+
source TEXT NOT NULL,
|
|
4
|
+
conversation_id TEXT NOT NULL,
|
|
5
|
+
date_key TEXT NOT NULL,
|
|
6
|
+
updated_at TEXT NOT NULL,
|
|
7
|
+
messages INTEGER NOT NULL DEFAULT 0,
|
|
8
|
+
messages_with_swears INTEGER NOT NULL DEFAULT 0,
|
|
9
|
+
swear_count INTEGER NOT NULL DEFAULT 0,
|
|
10
|
+
scanned_at TEXT NOT NULL,
|
|
11
|
+
UNIQUE (source, conversation_id, date_key)
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
CREATE INDEX IF NOT EXISTS psyche_devrage_conversation_measures_date_idx
|
|
15
|
+
ON psyche_devrage_conversation_measures (date_key DESC);
|
|
16
|
+
|
|
17
|
+
CREATE INDEX IF NOT EXISTS psyche_devrage_conversation_measures_updated_idx
|
|
18
|
+
ON psyche_devrage_conversation_measures (updated_at DESC);
|
|
19
|
+
|
|
20
|
+
CREATE TABLE IF NOT EXISTS psyche_devrage_metric_measures (
|
|
21
|
+
id TEXT PRIMARY KEY,
|
|
22
|
+
date_key TEXT NOT NULL,
|
|
23
|
+
metric_key TEXT NOT NULL,
|
|
24
|
+
value REAL NOT NULL,
|
|
25
|
+
unit TEXT NOT NULL DEFAULT '',
|
|
26
|
+
sample_count INTEGER NOT NULL DEFAULT 0,
|
|
27
|
+
computed_at TEXT NOT NULL,
|
|
28
|
+
UNIQUE (date_key, metric_key)
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
CREATE INDEX IF NOT EXISTS psyche_devrage_metric_measures_metric_date_idx
|
|
32
|
+
ON psyche_devrage_metric_measures (metric_key, date_key DESC);
|
|
33
|
+
|
|
34
|
+
CREATE TABLE IF NOT EXISTS psyche_devrage_sync_state (
|
|
35
|
+
id TEXT PRIMARY KEY CHECK (id = 'default'),
|
|
36
|
+
full_sync_completed_at TEXT,
|
|
37
|
+
last_daily_sync_at TEXT,
|
|
38
|
+
last_synced_date_key TEXT,
|
|
39
|
+
updated_at TEXT NOT NULL
|
|
40
|
+
);
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
CREATE TABLE IF NOT EXISTS health_workout_time_series (
|
|
2
|
+
id TEXT PRIMARY KEY,
|
|
3
|
+
workout_id TEXT NOT NULL REFERENCES health_workout_sessions(id) ON DELETE CASCADE,
|
|
4
|
+
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
5
|
+
source_sample_uid TEXT NOT NULL,
|
|
6
|
+
series_index INTEGER NOT NULL DEFAULT 0,
|
|
7
|
+
metric_key TEXT NOT NULL,
|
|
8
|
+
label TEXT NOT NULL DEFAULT '',
|
|
9
|
+
category TEXT NOT NULL DEFAULT '',
|
|
10
|
+
unit TEXT NOT NULL DEFAULT '',
|
|
11
|
+
value REAL NOT NULL,
|
|
12
|
+
started_at TEXT NOT NULL,
|
|
13
|
+
ended_at TEXT NOT NULL,
|
|
14
|
+
source_device TEXT NOT NULL DEFAULT '',
|
|
15
|
+
source_bundle_identifier TEXT,
|
|
16
|
+
source_product_type TEXT,
|
|
17
|
+
capture_method TEXT NOT NULL DEFAULT 'associated_workout',
|
|
18
|
+
quality_flags_json TEXT NOT NULL DEFAULT '[]',
|
|
19
|
+
metadata_json TEXT NOT NULL DEFAULT '{}',
|
|
20
|
+
provenance_json TEXT NOT NULL DEFAULT '{}',
|
|
21
|
+
created_at TEXT NOT NULL,
|
|
22
|
+
updated_at TEXT NOT NULL,
|
|
23
|
+
UNIQUE (workout_id, metric_key, source_sample_uid, series_index)
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
CREATE INDEX IF NOT EXISTS idx_health_workout_time_series_workout_metric
|
|
27
|
+
ON health_workout_time_series(workout_id, metric_key, started_at);
|
|
28
|
+
|
|
29
|
+
CREATE INDEX IF NOT EXISTS idx_health_workout_time_series_user_metric
|
|
30
|
+
ON health_workout_time_series(user_id, metric_key, started_at DESC);
|
|
31
|
+
|
|
32
|
+
CREATE TABLE IF NOT EXISTS health_workout_routes (
|
|
33
|
+
id TEXT PRIMARY KEY,
|
|
34
|
+
workout_id TEXT NOT NULL REFERENCES health_workout_sessions(id) ON DELETE CASCADE,
|
|
35
|
+
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
36
|
+
source_route_uid TEXT NOT NULL,
|
|
37
|
+
point_index INTEGER NOT NULL,
|
|
38
|
+
recorded_at TEXT NOT NULL,
|
|
39
|
+
latitude REAL NOT NULL,
|
|
40
|
+
longitude REAL NOT NULL,
|
|
41
|
+
altitude_meters REAL,
|
|
42
|
+
horizontal_accuracy_meters REAL,
|
|
43
|
+
vertical_accuracy_meters REAL,
|
|
44
|
+
speed_mps REAL,
|
|
45
|
+
course_degrees REAL,
|
|
46
|
+
metadata_json TEXT NOT NULL DEFAULT '{}',
|
|
47
|
+
provenance_json TEXT NOT NULL DEFAULT '{}',
|
|
48
|
+
created_at TEXT NOT NULL,
|
|
49
|
+
updated_at TEXT NOT NULL,
|
|
50
|
+
UNIQUE (workout_id, source_route_uid, point_index)
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
CREATE INDEX IF NOT EXISTS idx_health_workout_routes_workout
|
|
54
|
+
ON health_workout_routes(workout_id, point_index);
|
|
55
|
+
|
|
56
|
+
CREATE TABLE IF NOT EXISTS health_zone_profiles (
|
|
57
|
+
id TEXT PRIMARY KEY,
|
|
58
|
+
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
59
|
+
model_version TEXT NOT NULL DEFAULT 'forge-hrr-v1',
|
|
60
|
+
birth_year INTEGER,
|
|
61
|
+
sex_at_birth TEXT,
|
|
62
|
+
known_max_hr REAL,
|
|
63
|
+
threshold_hr REAL,
|
|
64
|
+
resting_hr_override REAL,
|
|
65
|
+
custom_zones_json TEXT NOT NULL DEFAULT '[]',
|
|
66
|
+
inferred_max_hr REAL,
|
|
67
|
+
inferred_resting_hr REAL,
|
|
68
|
+
confidence TEXT NOT NULL DEFAULT 'medium',
|
|
69
|
+
thresholds_json TEXT NOT NULL DEFAULT '[]',
|
|
70
|
+
metadata_json TEXT NOT NULL DEFAULT '{}',
|
|
71
|
+
created_at TEXT NOT NULL,
|
|
72
|
+
updated_at TEXT NOT NULL,
|
|
73
|
+
UNIQUE(user_id, model_version)
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
CREATE TABLE IF NOT EXISTS health_workout_analytics (
|
|
77
|
+
id TEXT PRIMARY KEY,
|
|
78
|
+
workout_id TEXT NOT NULL REFERENCES health_workout_sessions(id) ON DELETE CASCADE,
|
|
79
|
+
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
80
|
+
zone_profile_id TEXT REFERENCES health_zone_profiles(id) ON DELETE SET NULL,
|
|
81
|
+
model_version TEXT NOT NULL DEFAULT 'forge-hrr-v1',
|
|
82
|
+
confidence TEXT NOT NULL DEFAULT 'unavailable',
|
|
83
|
+
data_quality_json TEXT NOT NULL DEFAULT '{}',
|
|
84
|
+
zone_durations_json TEXT NOT NULL DEFAULT '[]',
|
|
85
|
+
hr_summary_json TEXT NOT NULL DEFAULT '{}',
|
|
86
|
+
load_json TEXT NOT NULL DEFAULT '{}',
|
|
87
|
+
route_summary_json TEXT NOT NULL DEFAULT '{}',
|
|
88
|
+
computed_at TEXT NOT NULL,
|
|
89
|
+
created_at TEXT NOT NULL,
|
|
90
|
+
updated_at TEXT NOT NULL,
|
|
91
|
+
UNIQUE(workout_id, model_version)
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
CREATE INDEX IF NOT EXISTS idx_health_workout_analytics_user
|
|
95
|
+
ON health_workout_analytics(user_id, computed_at DESC);
|
|
@@ -46,6 +46,7 @@ import { getInsightsPayload } from "./services/insights.js";
|
|
|
46
46
|
import { buildLifeForcePayload, createFatigueSignal, listLifeForceTemplates, resolveLifeForceUser, updateLifeForceProfile, updateLifeForceTemplate } from "./services/life-force.js";
|
|
47
47
|
import { createEntities, deleteEntities, deleteEntity, getSettingsBinPayload, restoreEntities, searchEntities, updateEntities } from "./services/entity-crud.js";
|
|
48
48
|
import { getPsycheOverview } from "./services/psyche.js";
|
|
49
|
+
import { getPsycheMetricsViewData, syncDevrageMetricHistoryIfNeeded } from "./services/devrage.js";
|
|
49
50
|
import { exportPsycheObservationCalendar, getPsycheObservationCalendar } from "./services/psyche-observation-calendar.js";
|
|
50
51
|
import { getProjectBoard, getProjectSummary, listProjectSummaries } from "./services/projects.js";
|
|
51
52
|
import { createDataBackup, exportData, getDataManagementState, maybeRunAutomaticBackup, restoreDataBackup, scanForDataRecoveryCandidates, switchDataRoot, updateDataManagementSettings } from "./services/data-management.js";
|
|
@@ -65,7 +66,7 @@ import { registerWebRoutes } from "./web.js";
|
|
|
65
66
|
import { createManagerRuntime } from "./managers/runtime.js";
|
|
66
67
|
import { isManagerError } from "./managers/type-guards.js";
|
|
67
68
|
import { buildCompanionPairingTransport, getCompanionIrohStatus, stopCompanionIroh } from "./services/companion-iroh.js";
|
|
68
|
-
import { createCompanionPairingSession, createCompanionPairingSessionSchema, createSleepSession, createSleepSessionSchema, createWorkoutSession, createWorkoutSessionSchema, deleteSleepSession, deleteWorkoutSession, getCompanionPairingSessionById, getCompanionOverview, getFitnessViewData, getSleepSessionById, getSleepSessionDetailById, getSleepTimelineOverlaysForRange, getSleepViewData, getVitalsViewData, getWorkoutSessionById, heartbeatCompanionPairing, heartbeatCompanionPairingSchema, ingestMobileHealthSync, mobileHealthSyncSchema, patchCompanionPairingSourceState, patchCompanionPairingSourceStateSchema, companionSourceKeySchema, requireValidPairing, revokeAllCompanionPairingSessions, revokeAllCompanionPairingSessionsSchema, revokeCompanionPairingSession, updateMobileCompanionSourceState, updateMobileCompanionSourceStateSchema, verifyCompanionPairing, verifyCompanionPairingSchema, updateSleepMetadata, updateSleepMetadataSchema, updateWorkoutMetadata, updateWorkoutMetadataSchema } from "./health.js";
|
|
69
|
+
import { createCompanionPairingSession, createCompanionPairingSessionSchema, createSleepSession, createSleepSessionSchema, createWorkoutSession, createWorkoutSessionSchema, deleteSleepSession, deleteWorkoutSession, getCompanionPairingSessionById, getCompanionOverview, getFitnessViewData, getSleepSessionById, getSleepSessionDetailById, getSleepTimelineOverlaysForRange, getSleepViewData, getVitalsViewData, getHealthZoneProfileForUser, getWorkoutSessionById, getWorkoutSessionDetailById, heartbeatCompanionPairing, heartbeatCompanionPairingSchema, healthZoneProfilePatchSchema, ingestMobileHealthSync, mobileHealthSyncSchema, patchHealthZoneProfileForUser, patchCompanionPairingSourceState, patchCompanionPairingSourceStateSchema, companionSourceKeySchema, requireValidPairing, revokeAllCompanionPairingSessions, revokeAllCompanionPairingSessionsSchema, revokeCompanionPairingSession, updateMobileCompanionSourceState, updateMobileCompanionSourceStateSchema, verifyCompanionPairing, verifyCompanionPairingSchema, updateSleepMetadata, updateSleepMetadataSchema, updateWorkoutMetadata, updateWorkoutMetadataSchema } from "./health.js";
|
|
69
70
|
import { analyzeMovementUserBoxPreflight, createMovementUserBox, createMovementPlace, deleteMovementUserBox, getMovementAllTimeSummary, getMovementBoxDetail, getMovementDayDetail, getMovementMobileBootstrap, getMovementTimeline, getMovementSelectionAggregate, getMovementSettings, getMovementTripDetail, getMovementMonthSummary, invalidateAutomaticMovementBox, listMovementPlaces, movementAutomaticBoxInvalidateSchema, movementMobileBootstrapSchema, movementMobilePlaceMutationSchema, movementMobileStayPatchSchema, movementMobileUserBoxCreateSchema, movementMobileUserBoxPreflightSchema, movementMobileUserBoxPatchSchema, movementMobileAutomaticBoxInvalidateSchema, movementMobileTimelineSchema, movementPlaceMutationSchema, movementPlacePatchSchema, movementSelectionAggregateSchema, movementStayPatchSchema, movementTripPatchSchema, movementUserBoxCreateSchema, movementUserBoxPreflightSchema, movementUserBoxPatchSchema, movementSettingsPatchSchema, movementTimelineQuerySchema, movementTripPointPatchSchema, deleteMovementStay, deleteMovementTrip, deleteMovementTripPoint, updateMovementPlace, updateMovementSettings, updateMovementStay, updateMovementTrip, updateMovementUserBox, updateMovementTripPoint, resolveMovementTimelineSegmentForBox } from "./movement.js";
|
|
70
71
|
import { getScreenTimeAllTimeSummary, getScreenTimeDayDetail, getScreenTimeMonthSummary, getScreenTimeSettings, screenTimeSettingsPatchSchema, updateScreenTimeSettings } from "./screen-time.js";
|
|
71
72
|
import { assertWatchReady, buildWatchBootstrap, ingestWatchCaptureBatch, mobileWatchBootstrapSchema, mobileWatchCaptureBatchSchema, mobileWatchHabitCheckInSchema } from "./watch-mobile.js";
|
|
@@ -1957,14 +1958,15 @@ function classifyOnboardingEntity(entityType) {
|
|
|
1957
1958
|
entityType === "questionnaire_run" ||
|
|
1958
1959
|
entityType === "preference_judgment" ||
|
|
1959
1960
|
entityType === "preference_signal" ||
|
|
1960
|
-
entityType === "work_adjustment"
|
|
1961
|
+
entityType === "work_adjustment" ||
|
|
1962
|
+
entityType === "self_observation") {
|
|
1961
1963
|
return "action_workflow_entity";
|
|
1962
1964
|
}
|
|
1963
1965
|
return "read_model_only_surface";
|
|
1964
1966
|
}
|
|
1965
1967
|
function buildPreferredMutationPath(entityType) {
|
|
1966
1968
|
if (entityType in AGENT_ONBOARDING_BATCH_ROUTE_BASES) {
|
|
1967
|
-
return "/api/v1/entities/create | /api/v1/entities/update | /api/v1/entities/delete | /api/v1/entities/search";
|
|
1969
|
+
return "/api/v1/entities/create | /api/v1/entities/update | /api/v1/entities/delete | /api/v1/entities/restore | /api/v1/entities/search";
|
|
1968
1970
|
}
|
|
1969
1971
|
switch (entityType) {
|
|
1970
1972
|
case "wiki_page":
|
|
@@ -1986,7 +1988,7 @@ function buildPreferredMutationPath(entityType) {
|
|
|
1986
1988
|
case "life_force":
|
|
1987
1989
|
return "Use the dedicated Life Force route family for overview, profile edits, weekday templates, and fatigue signals.";
|
|
1988
1990
|
case "workbench":
|
|
1989
|
-
return "Use the dedicated Workbench route family for flow CRUD, execution, run history, published outputs, node results, and latest-node-output reads.";
|
|
1991
|
+
return "Use the dedicated Workbench route family for flow CRUD, execution, saved-flow chat follow-ups, run history, published outputs, node results, and latest-node-output reads.";
|
|
1990
1992
|
case "self_observation":
|
|
1991
1993
|
return "Read the calendar surface; mutate it by creating or updating note-backed observations with frontmatter.observedAt.";
|
|
1992
1994
|
case "sleep_overview":
|
|
@@ -1998,6 +2000,12 @@ function buildPreferredMutationPath(entityType) {
|
|
|
1998
2000
|
}
|
|
1999
2001
|
}
|
|
2000
2002
|
function buildPreferredMutationTool(entityType) {
|
|
2003
|
+
if (entityType === "sleep_session") {
|
|
2004
|
+
return "forge_create_entities | forge_update_entities | forge_delete_entities | forge_search_entities | forge_update_sleep_session for reflective enrichment after review";
|
|
2005
|
+
}
|
|
2006
|
+
if (entityType === "workout_session") {
|
|
2007
|
+
return "forge_create_entities | forge_update_entities | forge_delete_entities | forge_search_entities | forge_update_workout_session for reflective enrichment after review";
|
|
2008
|
+
}
|
|
2001
2009
|
if (entityType in AGENT_ONBOARDING_BATCH_ROUTE_BASES) {
|
|
2002
2010
|
return "forge_create_entities | forge_update_entities | forge_delete_entities | forge_search_entities";
|
|
2003
2011
|
}
|
|
@@ -2016,6 +2024,8 @@ function buildPreferredMutationTool(entityType) {
|
|
|
2016
2024
|
return "forge_submit_preferences_signal";
|
|
2017
2025
|
case "work_adjustment":
|
|
2018
2026
|
return "forge_adjust_work_minutes";
|
|
2027
|
+
case "self_observation":
|
|
2028
|
+
return "forge_get_self_observation_calendar | forge_create_entities | forge_update_entities";
|
|
2019
2029
|
case "movement":
|
|
2020
2030
|
return "forge_call_movement_route";
|
|
2021
2031
|
case "life_force":
|
|
@@ -2691,7 +2701,7 @@ const AGENT_ONBOARDING_ENTITY_CATALOG = [
|
|
|
2691
2701
|
}),
|
|
2692
2702
|
enrichOnboardingEntityGuide({
|
|
2693
2703
|
entityType: "movement",
|
|
2694
|
-
purpose: "The specialized Movement surface for day, month, all-time, timeline, trip, place, selection, and manual overlay work.",
|
|
2704
|
+
purpose: "The specialized Movement surface for day, month, all-time, timeline, trip, place, selection, settings, and manual overlay work.",
|
|
2695
2705
|
minimumCreateFields: [],
|
|
2696
2706
|
relationshipRules: [
|
|
2697
2707
|
"Movement is a specialized domain surface, not a normal batch CRUD entity family.",
|
|
@@ -2721,7 +2731,7 @@ const AGENT_ONBOARDING_ENTITY_CATALOG = [
|
|
|
2721
2731
|
}),
|
|
2722
2732
|
enrichOnboardingEntityGuide({
|
|
2723
2733
|
entityType: "workbench",
|
|
2724
|
-
purpose: "The specialized Workbench surface for flow catalog work, flow CRUD, execution, run history, published outputs, node results, and latest-node-output reads.",
|
|
2734
|
+
purpose: "The specialized Workbench surface for flow catalog work, flow CRUD, execution, saved-flow chat follow-ups, run history, published outputs, node results, and latest-node-output reads.",
|
|
2725
2735
|
minimumCreateFields: [],
|
|
2726
2736
|
relationshipRules: [
|
|
2727
2737
|
"Workbench is a specialized execution surface, not a normal batch CRUD entity family.",
|
|
@@ -3165,7 +3175,9 @@ const AGENT_ONBOARDING_ENTITY_CONVERSATION_PLAYBOOKS = [
|
|
|
3165
3175
|
"Ask whether the focus is a stay, a trip, a place, a timeline window, or a selected span.",
|
|
3166
3176
|
"Ask for the time window, place, or movement item that makes the question concrete.",
|
|
3167
3177
|
"Ask what they are trying to notice, preserve, or answer through that movement context.",
|
|
3178
|
+
"If the user is changing movement operating behavior, ask whether this is about passive tracking, publish mode, retention, or companion readiness.",
|
|
3168
3179
|
"Choose the dedicated day, month, all-time, timeline, places, trip-detail, or selection route once the question shape is clear.",
|
|
3180
|
+
"Use the dedicated settings route for passive capture, publish mode, and retention behavior instead of treating those as place, stay, or trip edits.",
|
|
3169
3181
|
"Use allTime for whole-history aggregates, selection for a bounded selected-span aggregate, and tripDetail only when a concrete trip id is known.",
|
|
3170
3182
|
"If the truth of one uncertain span is still unclear, read the timeline or saved-box detail before you mutate it.",
|
|
3171
3183
|
"Skip the meta lane question when the user already named the exact correction or review target and only one ambiguity remains.",
|
|
@@ -3206,6 +3218,9 @@ const AGENT_ONBOARDING_ENTITY_CONVERSATION_PLAYBOOKS = [
|
|
|
3206
3218
|
"Use listFlows for the saved flow catalog and boxCatalog for available input-box contracts; do not collapse both into a vague catalog read.",
|
|
3207
3219
|
"Ask which flow, slug, run, or node the request is about.",
|
|
3208
3220
|
"Ask whether they need the stable flow contract, one run result, one published output, one node result, or the latest node output.",
|
|
3221
|
+
"If the user is creating or editing a flow, clarify what the flow should reliably produce, what input contract it should accept, and what smallest structural change is intended before asking for node details.",
|
|
3222
|
+
"If the user wants to delete or archive a flow, confirm the saved flow and whether published outputs or run history need to be preserved elsewhere before calling the delete route.",
|
|
3223
|
+
"If the user wants to send a follow-up into a saved flow chat, confirm the saved flow and what the message should accomplish instead of treating it as a new run or note.",
|
|
3209
3224
|
"If the user already named the flow and action clearly, skip the meta lane question and ask only for the missing run, node, or output scope.",
|
|
3210
3225
|
"If the user wants a stable public input contract or published output, prefer those dedicated reads instead of detouring through run history first.",
|
|
3211
3226
|
"If the user is still shaping a payload or edit, prefer flow detail or box catalog reads before asking for structured inputs.",
|
|
@@ -3417,10 +3432,11 @@ const AGENT_ONBOARDING_PSYCHE_PLAYBOOKS = [
|
|
|
3417
3432
|
coachingGoal: "Help the user describe how the mode shows up, what it is trying to do, what it fears, and what burden it carries, rather than reducing it to a label only.",
|
|
3418
3433
|
askSequence: [
|
|
3419
3434
|
"Start with a recent moment when this part-state took over.",
|
|
3420
|
-
"Choose the mode family once the lived description is clearer.",
|
|
3421
|
-
"Name the mode in the user's language.",
|
|
3422
3435
|
"Describe the felt persona, body posture, imagery, or symbolic form.",
|
|
3423
|
-
"Clarify
|
|
3436
|
+
"Clarify what it is trying to protect, prevent, control, or force.",
|
|
3437
|
+
"Clarify its fear, burden, and protective job before choosing a family label.",
|
|
3438
|
+
"Offer one candidate name or formulation in the user's language and invite correction.",
|
|
3439
|
+
"Choose the mode family only after the lived description, protective job, fear, or burden is visible enough.",
|
|
3424
3440
|
"Explore when it first became necessary or familiar.",
|
|
3425
3441
|
"Notice linked patterns, behaviors, values, and what a healthy-adult response would need to do."
|
|
3426
3442
|
],
|
|
@@ -4367,8 +4383,9 @@ function buildAgentOnboardingPayload(request) {
|
|
|
4367
4383
|
aliases: ["movement", "Movement"],
|
|
4368
4384
|
summary: "Dedicated movement workspace API. Use these routes for stays, trips, time-in-place questions, visited places, trip detail, selection aggregates, user-defined overlays, and repair actions on already-recorded movement data.",
|
|
4369
4385
|
routeSelectionQuestions: [
|
|
4370
|
-
"Is the user asking for a day, month, all-time, timeline, place, trip detail,
|
|
4386
|
+
"Is the user asking for a day, month, all-time, timeline, place, trip detail, selected-span, or settings answer?",
|
|
4371
4387
|
"Is this a missing-gap overlay, a saved-overlay repair, or an edit to one already-recorded stay, trip, or trip point?",
|
|
4388
|
+
"If this is about operating behavior, is the change about passive tracking, publish mode, retention, or companion readiness?",
|
|
4372
4389
|
"If the target is already known, what one time, place, or saved-object detail is still missing before acting?"
|
|
4373
4390
|
],
|
|
4374
4391
|
methodRoutes: {
|
|
@@ -4427,6 +4444,7 @@ function buildAgentOnboardingPayload(request) {
|
|
|
4427
4444
|
"Movement is not a normal batch CRUD entity family. It is a dedicated record of stays and trips: a stay means the user remained in the same place for a span of time, and a trip means they traveled between places.",
|
|
4428
4445
|
"Route-selection questions are internal. User-facing questions should ask for the useful time window, place, selected span, stay, or trip instead of reciting day/month/all-time/timeline/selection route keys.",
|
|
4429
4446
|
"Use /api/v1/movement/day, /month, /all-time, /timeline, or /selection when the user wants behavioral answers such as how long they stayed at home, when they traveled, which places dominated a period, or what happened across a selected span.",
|
|
4447
|
+
"Use GET /api/v1/movement/settings and PATCH /api/v1/movement/settings when the user wants to inspect or change passive capture, publish mode, retention mode, or companion readiness. Do not route settings changes through stays, trips, places, or batch CRUD.",
|
|
4430
4448
|
"Use the movement write routes when the user wants to add a place or manual overlay, update a specific stay or trip, repair one recorded movement span, or attach movement context to another Forge record. If the user is filling a missing-data gap, the usual write path is a user-defined overlay box rather than a raw stay or trip patch.",
|
|
4431
4449
|
"If the user is revising or removing an existing correction, first identify whether the saved object is a user-defined box, automatic box, recorded stay, recorded trip, or trip point so the repair or delete path stays truthful.",
|
|
4432
4450
|
"For an explicit statement like 'that missing block was me staying home', do not reopen broad intake. Preflight only if timing overlap is unclear, then create a user-defined `stay` box for that interval and read the updated timeline back."
|
|
@@ -4504,8 +4522,10 @@ function buildAgentOnboardingPayload(request) {
|
|
|
4504
4522
|
aliases: ["workbench", "Workbench"],
|
|
4505
4523
|
summary: "Dedicated graph-flow API. Use it for flow catalog reads, flow CRUD, execution, run history, published outputs, node results, and latest successful node outputs.",
|
|
4506
4524
|
routeSelectionQuestions: [
|
|
4507
|
-
"Is the job flow discovery, flow editing, execution, run history, published output, run detail, node result, latest node output, or flow chat follow-up?",
|
|
4525
|
+
"Is the job flow discovery, flow creation, flow editing, flow deletion, execution, run history, published output, run detail, node result, latest node output, or flow chat follow-up?",
|
|
4508
4526
|
"Does the user need a stable public contract or one execution artifact?",
|
|
4527
|
+
"For flow CRUD, what stable input contract, expected output, or lifecycle effect must stay true?",
|
|
4528
|
+
"For flow chat follow-up, which saved flow should receive the message and what should the message accomplish?",
|
|
4509
4529
|
"If the flow is already known, what one run, node, or output scope detail is still missing before acting?"
|
|
4510
4530
|
],
|
|
4511
4531
|
methodRoutes: {
|
|
@@ -4551,6 +4571,10 @@ function buildAgentOnboardingPayload(request) {
|
|
|
4551
4571
|
"Route-selection questions are internal. User-facing questions should ask whether the user needs the saved flow, its input contract, one run, one node, or the public result instead of reciting Workbench route keys.",
|
|
4552
4572
|
"Use the flow routes when the agent needs stable public input contracts, published outputs, node-level results, or reusable execution history.",
|
|
4553
4573
|
"If the user is still figuring out inputs or editable structure, read flow detail or box catalog before asking them to author a payload from memory.",
|
|
4574
|
+
"For flow creation, clarify what the flow should reliably produce, which input contract it should accept, and which first node or box anchors the flow before asking for structured payload details.",
|
|
4575
|
+
"For flow edits, ask what behavior should change while preserving the public contract unless the user explicitly wants the contract changed.",
|
|
4576
|
+
"For flow deletion, confirm the saved flow and whether published outputs or run history need preservation elsewhere before using the delete route.",
|
|
4577
|
+
"For saved flow chat follow-ups, use POST /api/v1/workbench/flows/:id/chat only when the user wants to continue a flow-specific conversation. Do not turn that into a new run, note, or generic entity update unless the user asks.",
|
|
4554
4578
|
"Prefer the dedicated output and node-result routes over reverse-engineering raw traces.",
|
|
4555
4579
|
"If the user only wants a published output, latest node output, or run detail, do not reopen a flow-edit intake before reading that artifact.",
|
|
4556
4580
|
"If the user already named the flow and wants one output or one run, skip the broad lane question and ask only for the missing run, node, or output scope."
|
|
@@ -4814,6 +4838,8 @@ function buildAgentOnboardingPayload(request) {
|
|
|
4814
4838
|
movementTimeline: '{"routeKey":"timeline","query":{"from":"2026-05-01T00:00:00.000Z","to":"2026-05-06T23:59:59.999Z","userIds":["user_operator"]}}',
|
|
4815
4839
|
movementSelection: '{"routeKey":"selection","query":{"from":"2026-05-01T00:00:00.000Z","to":"2026-05-14T23:59:59.999Z","placeIds":["place_home"],"userIds":["user_operator"]}}',
|
|
4816
4840
|
movementTripDetail: '{"routeKey":"tripDetail","pathParams":{"id":"trip_123"}}',
|
|
4841
|
+
movementSettings: '{"routeKey":"settings","query":{"userIds":["user_operator"]}}',
|
|
4842
|
+
movementSettingsUpdate: '{"routeKey":"settingsUpdate","body":{"trackingEnabled":true,"publishMode":"draft_review","retentionMode":"aggregates_only"}}',
|
|
4817
4843
|
movementMissingStayPreflight: '{"routeKey":"userBoxPreflight","body":{"kind":"stay","startedAt":"2026-05-06T13:00:00.000Z","endedAt":"2026-05-06T15:00:00.000Z","placeLabel":"Home","userId":"user_operator"}}',
|
|
4818
4844
|
movementMissingStayCreate: '{"routeKey":"userBoxCreate","body":{"kind":"stay","startedAt":"2026-05-06T13:00:00.000Z","endedAt":"2026-05-06T15:00:00.000Z","placeLabel":"Home","userId":"user_operator","note":"Manual correction after reviewing the timeline."}}',
|
|
4819
4845
|
lifeForceOverview: '{"routeKey":"overview"}',
|
|
@@ -4822,10 +4848,14 @@ function buildAgentOnboardingPayload(request) {
|
|
|
4822
4848
|
lifeForceFatigueSignal: '{"routeKey":"fatigueSignal","body":{"signal":"tired","intensity":7,"note":"Sharp post-lunch dip after clinic admin."}}',
|
|
4823
4849
|
workbenchFlowCatalog: '{"routeKey":"listFlows","query":{"includeArchived":false}}',
|
|
4824
4850
|
workbenchBoxCatalog: '{"routeKey":"boxCatalog"}',
|
|
4851
|
+
workbenchCreateFlow: '{"routeKey":"createFlow","body":{"title":"Research digest","slug":"research-digest","description":"Turn a topic into a cited digest with a stable published summary.","nodes":[],"edges":[]}}',
|
|
4852
|
+
workbenchUpdateFlow: '{"routeKey":"updateFlow","pathParams":{"id":"flow_research_digest"},"body":{"description":"Keep the same input contract but add a stronger evidence-check node."}}',
|
|
4853
|
+
workbenchDeleteFlow: '{"routeKey":"deleteFlow","pathParams":{"id":"flow_research_digest"}}',
|
|
4825
4854
|
workbenchRunDetail: '{"routeKey":"runDetail","pathParams":{"id":"flow_research_digest","runId":"run_123"}}',
|
|
4826
4855
|
workbenchPublishedOutput: '{"routeKey":"publishedOutput","pathParams":{"id":"flow_research_digest"}}',
|
|
4827
4856
|
workbenchLatestNodeOutput: '{"routeKey":"latestNodeOutput","pathParams":{"id":"flow_research_digest","nodeId":"node_summary"}}',
|
|
4828
|
-
workbenchRunFlow: '{"routeKey":"runFlow","pathParams":{"id":"flow_research_digest"},"body":{"input":{"topic":"question flow quality"}}}'
|
|
4857
|
+
workbenchRunFlow: '{"routeKey":"runFlow","pathParams":{"id":"flow_research_digest"},"body":{"input":{"topic":"question flow quality"}}}',
|
|
4858
|
+
workbenchChatFlow: '{"routeKey":"chatFlow","pathParams":{"id":"flow_research_digest"},"body":{"message":"Refine the summary around API route risks and keep the published output stable."}}'
|
|
4829
4859
|
}
|
|
4830
4860
|
}
|
|
4831
4861
|
};
|
|
@@ -5387,6 +5417,12 @@ function buildOperatorOverviewRouteGuide() {
|
|
|
5387
5417
|
summary: "Aggregate Psyche state across values, patterns, behaviors, beliefs, modes, and trigger reports.",
|
|
5388
5418
|
requiredScope: "psyche.read"
|
|
5389
5419
|
},
|
|
5420
|
+
{
|
|
5421
|
+
id: "psyche_metrics",
|
|
5422
|
+
path: "/api/v1/psyche/metrics",
|
|
5423
|
+
summary: "Daily Psyche metric read model with stored devrage history and summary statistics.",
|
|
5424
|
+
requiredScope: "psyche.read"
|
|
5425
|
+
},
|
|
5390
5426
|
{
|
|
5391
5427
|
id: "xp_metrics",
|
|
5392
5428
|
path: "/api/v1/metrics/xp",
|
|
@@ -6221,10 +6257,27 @@ export async function buildServer(options = {}) {
|
|
|
6221
6257
|
void maybeRunAutomaticBackup().catch(() => {
|
|
6222
6258
|
// Ignore startup backup failures; the Data settings surface exposes recovery.
|
|
6223
6259
|
});
|
|
6260
|
+
const devrageMetricSyncEnabled = options.devrageMetricSync ?? !options.dataRoot;
|
|
6261
|
+
const devrageMetricTimer = devrageMetricSyncEnabled
|
|
6262
|
+
? setInterval(() => {
|
|
6263
|
+
void syncDevrageMetricHistoryIfNeeded().catch(() => {
|
|
6264
|
+
// Devrage is a local metric import; failures should not break Forge.
|
|
6265
|
+
});
|
|
6266
|
+
}, 60 * 60 * 1000)
|
|
6267
|
+
: null;
|
|
6268
|
+
devrageMetricTimer?.unref?.();
|
|
6269
|
+
if (devrageMetricSyncEnabled) {
|
|
6270
|
+
void syncDevrageMetricHistoryIfNeeded().catch(() => {
|
|
6271
|
+
// The Psyche metric payload exposes an empty state until sync succeeds.
|
|
6272
|
+
});
|
|
6273
|
+
}
|
|
6224
6274
|
app.addHook("onClose", async () => {
|
|
6225
6275
|
clearInterval(diagnosticRetentionTimer);
|
|
6226
6276
|
clearInterval(cronSchedulerTimer);
|
|
6227
6277
|
clearInterval(dataBackupTimer);
|
|
6278
|
+
if (devrageMetricTimer) {
|
|
6279
|
+
clearInterval(devrageMetricTimer);
|
|
6280
|
+
}
|
|
6228
6281
|
taskRunWatchdog?.stop();
|
|
6229
6282
|
await stopCompanionIroh();
|
|
6230
6283
|
await managers.backgroundJobs.stop();
|
|
@@ -6744,12 +6797,37 @@ export async function buildServer(options = {}) {
|
|
|
6744
6797
|
app.get("/api/v1/health/vitals", async (request) => ({
|
|
6745
6798
|
vitals: getVitalsViewData(resolveScopedUserIds(request.query))
|
|
6746
6799
|
}));
|
|
6800
|
+
app.get("/api/v1/health/zone-profile", async (request) => {
|
|
6801
|
+
const userIds = resolveScopedUserIds(request.query);
|
|
6802
|
+
return {
|
|
6803
|
+
zoneProfile: getHealthZoneProfileForUser(userIds?.[0] ?? "user_operator")
|
|
6804
|
+
};
|
|
6805
|
+
});
|
|
6806
|
+
app.patch("/api/v1/health/zone-profile", async (request) => {
|
|
6807
|
+
const auth = requireScopedAccess(request.headers, ["write"], { route: "/api/v1/health/zone-profile" });
|
|
6808
|
+
const userIds = resolveScopedUserIds(request.query);
|
|
6809
|
+
void auth;
|
|
6810
|
+
return {
|
|
6811
|
+
zoneProfile: patchHealthZoneProfileForUser(userIds?.[0] ?? "user_operator", healthZoneProfilePatchSchema.parse(request.body ?? {}))
|
|
6812
|
+
};
|
|
6813
|
+
});
|
|
6747
6814
|
app.post("/api/v1/health/workouts", async (request, reply) => {
|
|
6748
6815
|
const auth = requireScopedAccess(request.headers, ["write"], { route: "/api/v1/health/workouts" });
|
|
6749
6816
|
const workout = createWorkoutSession(createWorkoutSessionSchema.parse(request.body ?? {}), toActivityContext(auth));
|
|
6750
6817
|
reply.code(201);
|
|
6751
6818
|
return { workout };
|
|
6752
6819
|
});
|
|
6820
|
+
app.get("/api/v1/health/workouts/:id/detail", async (request, reply) => {
|
|
6821
|
+
const { id } = request.params;
|
|
6822
|
+
const query = request.query;
|
|
6823
|
+
const resolution = query.resolution === "raw" ? "raw" : "adaptive";
|
|
6824
|
+
const detail = getWorkoutSessionDetailById(id, resolution);
|
|
6825
|
+
if (!detail) {
|
|
6826
|
+
reply.code(404);
|
|
6827
|
+
return { error: "Workout session not found" };
|
|
6828
|
+
}
|
|
6829
|
+
return detail;
|
|
6830
|
+
});
|
|
6753
6831
|
app.get("/api/v1/health/workouts/:id", async (request, reply) => {
|
|
6754
6832
|
const { id } = request.params;
|
|
6755
6833
|
const workout = getWorkoutSessionById(id);
|
|
@@ -7337,6 +7415,10 @@ export async function buildServer(options = {}) {
|
|
|
7337
7415
|
const userIds = resolveScopedUserIds(request.query);
|
|
7338
7416
|
return { overview: getPsycheOverview(userIds) };
|
|
7339
7417
|
});
|
|
7418
|
+
app.get("/api/v1/psyche/metrics", async (request) => {
|
|
7419
|
+
requirePsycheScopedAccess(request.headers, ["psyche.read"], { route: "/api/v1/psyche/metrics" });
|
|
7420
|
+
return { metrics: getPsycheMetricsViewData() };
|
|
7421
|
+
});
|
|
7340
7422
|
app.get("/api/v1/psyche/questionnaires", async (request) => {
|
|
7341
7423
|
requirePsycheScopedAccess(request.headers, ["psyche.read"], { route: "/api/v1/psyche/questionnaires" });
|
|
7342
7424
|
const userIds = resolveScopedUserIds(request.query);
|