forge-openclaw-plugin 0.2.26 → 0.2.28

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.
Files changed (109) hide show
  1. package/README.md +60 -3
  2. package/dist/assets/{board-ta0rUHOf.js → board-DPFvZf-D.js} +2 -2
  3. package/dist/assets/{board-ta0rUHOf.js.map → board-DPFvZf-D.js.map} +1 -1
  4. package/dist/assets/index-Auw3JrdE.css +1 -0
  5. package/dist/assets/index-D1H7myQH.js +85 -0
  6. package/dist/assets/index-D1H7myQH.js.map +1 -0
  7. package/dist/assets/knowledge-graph-layout.worker-DRvzPxhP.js +2 -0
  8. package/dist/assets/knowledge-graph-layout.worker-DRvzPxhP.js.map +1 -0
  9. package/dist/assets/{motion-fBKPB6yw.js → motion-Bvwc85ch.js} +2 -2
  10. package/dist/assets/{motion-fBKPB6yw.js.map → motion-Bvwc85ch.js.map} +1 -1
  11. package/dist/assets/{table-C-IGTQni.js → table-FJQTJvUR.js} +2 -2
  12. package/dist/assets/{table-C-IGTQni.js.map → table-FJQTJvUR.js.map} +1 -1
  13. package/dist/assets/{ui-DInOpaYF.js → ui-GXFcgvSw.js} +2 -2
  14. package/dist/assets/{ui-DInOpaYF.js.map → ui-GXFcgvSw.js.map} +1 -1
  15. package/dist/assets/vendor-Cwf49UMz.js +1247 -0
  16. package/dist/assets/vendor-Cwf49UMz.js.map +1 -0
  17. package/dist/index.html +7 -7
  18. package/dist/openclaw/local-runtime.js +16 -0
  19. package/dist/openclaw/routes.d.ts +27 -0
  20. package/dist/openclaw/routes.js +16 -12
  21. package/dist/server/server/migrations/037_workbench_public_inputs_and_run_inputs.sql +5 -0
  22. package/dist/server/server/migrations/038_data_management_settings.sql +11 -0
  23. package/dist/server/server/migrations/039_life_force_and_action_points.sql +114 -0
  24. package/dist/server/server/migrations/040_screen_time_domain.sql +89 -0
  25. package/dist/server/server/migrations/041_companion_source_states.sql +21 -0
  26. package/dist/server/server/migrations/042_movement_boxes.sql +47 -0
  27. package/dist/server/server/migrations/043_movement_box_overlap_overrides.sql +26 -0
  28. package/dist/server/server/src/app.js +1900 -91
  29. package/dist/server/server/src/connectors/box-registry.js +44 -9
  30. package/dist/server/server/src/data-management-types.js +107 -0
  31. package/dist/server/server/src/db.js +68 -4
  32. package/dist/server/server/src/demo-data.js +2 -2
  33. package/dist/server/server/src/health.js +702 -18
  34. package/dist/server/server/src/managers/platform/llm-manager.js +7 -4
  35. package/dist/server/server/src/managers/platform/mock-workbench-provider.js +149 -0
  36. package/dist/server/server/src/managers/platform/secrets-manager.js +18 -1
  37. package/dist/server/server/src/managers/runtime.js +9 -0
  38. package/dist/server/server/src/movement.js +1971 -112
  39. package/dist/server/server/src/openapi.js +1390 -105
  40. package/dist/server/server/src/psyche-types.js +9 -1
  41. package/dist/server/server/src/repositories/activity-events.js +8 -0
  42. package/dist/server/server/src/repositories/ai-connectors.js +522 -74
  43. package/dist/server/server/src/repositories/calendar.js +151 -0
  44. package/dist/server/server/src/repositories/habits.js +37 -1
  45. package/dist/server/server/src/repositories/model-settings.js +13 -3
  46. package/dist/server/server/src/repositories/notes.js +3 -0
  47. package/dist/server/server/src/repositories/settings.js +380 -18
  48. package/dist/server/server/src/repositories/tasks.js +170 -10
  49. package/dist/server/server/src/runtime-data-root.js +82 -0
  50. package/dist/server/server/src/screen-time.js +802 -0
  51. package/dist/server/server/src/services/data-management.js +788 -0
  52. package/dist/server/server/src/services/entity-crud.js +205 -2
  53. package/dist/server/server/src/services/knowledge-graph.js +1455 -0
  54. package/dist/server/server/src/services/life-force-model.js +217 -0
  55. package/dist/server/server/src/services/life-force.js +2506 -0
  56. package/dist/server/server/src/services/psyche-observation-calendar.js +383 -16
  57. package/dist/server/server/src/types.js +307 -14
  58. package/dist/server/server/src/web.js +228 -13
  59. package/dist/server/src/components/customization/utility-widgets.js +136 -27
  60. package/dist/server/src/components/ui/info-tooltip.js +25 -0
  61. package/dist/server/src/components/workbench-boxes/calendar/calendar-boxes.js +78 -0
  62. package/dist/server/src/components/workbench-boxes/goals/goals-boxes.js +62 -0
  63. package/dist/server/src/components/workbench-boxes/habits/habits-boxes.js +62 -0
  64. package/dist/server/src/components/workbench-boxes/health/health-boxes.js +63 -8
  65. package/dist/server/src/components/workbench-boxes/insights/insights-boxes.js +50 -0
  66. package/dist/server/src/components/workbench-boxes/kanban/kanban-boxes.js +62 -54
  67. package/dist/server/src/components/workbench-boxes/movement/movement-boxes.js +18 -8
  68. package/dist/server/src/components/workbench-boxes/notes/notes-boxes.js +56 -38
  69. package/dist/server/src/components/workbench-boxes/overview/overview-boxes.js +65 -0
  70. package/dist/server/src/components/workbench-boxes/preferences/preferences-boxes.js +78 -0
  71. package/dist/server/src/components/workbench-boxes/projects/projects-boxes.js +35 -30
  72. package/dist/server/src/components/workbench-boxes/psyche/psyche-boxes.js +88 -0
  73. package/dist/server/src/components/workbench-boxes/questionnaires/questionnaires-boxes.js +61 -0
  74. package/dist/server/src/components/workbench-boxes/review/review-boxes.js +53 -0
  75. package/dist/server/src/components/workbench-boxes/shared/define-workbench-box.js +3 -1
  76. package/dist/server/src/components/workbench-boxes/shared/generic-node-view.js +39 -3
  77. package/dist/server/src/components/workbench-boxes/strategies/strategies-boxes.js +62 -0
  78. package/dist/server/src/components/workbench-boxes/tasks/tasks-boxes.js +76 -0
  79. package/dist/server/src/components/workbench-boxes/today/today-boxes.js +47 -32
  80. package/dist/server/src/components/workbench-boxes/wiki/wiki-boxes.js +60 -0
  81. package/dist/server/src/lib/api.js +280 -21
  82. package/dist/server/src/lib/data-management-types.js +1 -0
  83. package/dist/server/src/lib/entity-visuals.js +279 -0
  84. package/dist/server/src/lib/knowledge-graph-types.js +276 -0
  85. package/dist/server/src/lib/knowledge-graph.js +470 -0
  86. package/dist/server/src/lib/schemas.js +4 -0
  87. package/dist/server/src/lib/snapshot-normalizer.js +45 -1
  88. package/dist/server/src/lib/workbench/contracts.js +229 -0
  89. package/dist/server/src/lib/workbench/nodes.js +200 -0
  90. package/dist/server/src/lib/workbench/registry.js +52 -5
  91. package/dist/server/src/lib/workbench/runtime.js +254 -38
  92. package/dist/server/src/lib/workbench/tool-catalog.js +68 -0
  93. package/openclaw.plugin.json +1 -1
  94. package/package.json +1 -1
  95. package/server/migrations/037_workbench_public_inputs_and_run_inputs.sql +5 -0
  96. package/server/migrations/038_data_management_settings.sql +11 -0
  97. package/server/migrations/039_life_force_and_action_points.sql +114 -0
  98. package/server/migrations/040_screen_time_domain.sql +89 -0
  99. package/server/migrations/041_companion_source_states.sql +21 -0
  100. package/server/migrations/042_movement_boxes.sql +47 -0
  101. package/server/migrations/043_movement_box_overlap_overrides.sql +26 -0
  102. package/skills/forge-openclaw/SKILL.md +41 -11
  103. package/skills/forge-openclaw/entity_conversation_playbooks.md +448 -34
  104. package/skills/forge-openclaw/psyche_entity_playbooks.md +170 -17
  105. package/dist/assets/index-Ro0ZF_az.css +0 -1
  106. package/dist/assets/index-ytlpSj23.js +0 -79
  107. package/dist/assets/index-ytlpSj23.js.map +0 -1
  108. package/dist/assets/vendor-lE3tZJcC.js +0 -876
  109. package/dist/assets/vendor-lE3tZJcC.js.map +0 -1
@@ -0,0 +1,1455 @@
1
+ import { getDatabase } from "../db.js";
2
+ import { listInsights } from "../repositories/collaboration.js";
3
+ import { listCalendarEvents, listTaskTimeboxes, listWorkBlockTemplates } from "../repositories/calendar.js";
4
+ import { filterOwnedEntities } from "../repositories/entity-ownership.js";
5
+ import { listGoals } from "../repositories/goals.js";
6
+ import { listHabits } from "../repositories/habits.js";
7
+ import { listNotes } from "../repositories/notes.js";
8
+ import { listBehaviors, listBehaviorPatterns, listBeliefEntries, listEmotionDefinitions, listEventTypes, listModeGuideSessions, listModeProfiles, listPsycheValues, listTriggerReports } from "../repositories/psyche.js";
9
+ import { listStrategies } from "../repositories/strategies.js";
10
+ import { listTags } from "../repositories/tags.js";
11
+ import { listTasks } from "../repositories/tasks.js";
12
+ import { listWikiSpaces } from "../repositories/wiki-memory.js";
13
+ import { listProjectSummaries } from "./projects.js";
14
+ import { listAiConnectors } from "../repositories/ai-connectors.js";
15
+ import { buildKnowledgeGraphFacets, buildKnowledgeGraphFocusPayload, filterKnowledgeGraphData, selectKnowledgeGraphVisibleNodeIds } from "../../../src/lib/knowledge-graph.js";
16
+ import { getEntityVisual } from "../../../src/lib/entity-visuals.js";
17
+ import { KNOWLEDGE_GRAPH_RELATION_FAMILY_MAP, buildKnowledgeGraphFocusHref, buildKnowledgeGraphNodeId, getKnowledgeGraphEntityHref } from "../../../src/lib/knowledge-graph-types.js";
18
+ const GRAPH_RANGE = {
19
+ from: "2000-01-01T00:00:00.000Z",
20
+ to: "2100-01-01T00:00:00.000Z"
21
+ };
22
+ const BASE_NODE_SIZE = {
23
+ goal: 56,
24
+ strategy: 52,
25
+ project: 48,
26
+ task: 42,
27
+ tag: 30,
28
+ wiki_space: 40,
29
+ wiki_page: 38,
30
+ note: 34,
31
+ habit: 36,
32
+ insight: 34,
33
+ calendar_event: 34,
34
+ work_block: 32,
35
+ timebox: 33,
36
+ value: 38,
37
+ pattern: 38,
38
+ behavior: 38,
39
+ belief: 38,
40
+ mode: 38,
41
+ mode_session: 34,
42
+ report: 40,
43
+ event_type: 32,
44
+ emotion: 30,
45
+ workbench: 42,
46
+ functor: 36,
47
+ chat: 36
48
+ };
49
+ const WORKBENCH_SURFACE_ROUTES = {
50
+ workbench: {
51
+ title: "Workbench",
52
+ subtitle: "Global graph flows",
53
+ href: "/workbench"
54
+ },
55
+ overview: {
56
+ title: "Overview Surface",
57
+ subtitle: "Operator overview workspace",
58
+ href: "/overview"
59
+ },
60
+ today: {
61
+ title: "Today Surface",
62
+ subtitle: "Execution workspace",
63
+ href: "/today"
64
+ },
65
+ goals: {
66
+ title: "Goals Surface",
67
+ subtitle: "Goal planning workspace",
68
+ href: "/goals"
69
+ },
70
+ projects: {
71
+ title: "Projects Surface",
72
+ subtitle: "Project execution workspace",
73
+ href: "/projects"
74
+ },
75
+ kanban: {
76
+ title: "Kanban Surface",
77
+ subtitle: "Task board workspace",
78
+ href: "/kanban"
79
+ },
80
+ notes: {
81
+ title: "Notes Surface",
82
+ subtitle: "Notes and evidence workspace",
83
+ href: "/notes"
84
+ },
85
+ calendar: {
86
+ title: "Calendar Surface",
87
+ subtitle: "Calendar planning workspace",
88
+ href: "/calendar"
89
+ },
90
+ psyche: {
91
+ title: "Psyche Surface",
92
+ subtitle: "Psyche workspace",
93
+ href: "/psyche"
94
+ },
95
+ wiki: {
96
+ title: "KarpaWiki Surface",
97
+ subtitle: "KarpaWiki memory workspace",
98
+ href: "/wiki"
99
+ },
100
+ strategies: {
101
+ title: "Strategies Surface",
102
+ subtitle: "Strategy design workspace",
103
+ href: "/strategies"
104
+ },
105
+ preferences: {
106
+ title: "Preferences Surface",
107
+ subtitle: "Preference workspace",
108
+ href: "/preferences"
109
+ },
110
+ questionnaires: {
111
+ title: "Questionnaires Surface",
112
+ subtitle: "Questionnaire workspace",
113
+ href: "/questionnaires"
114
+ },
115
+ review: {
116
+ title: "Review Surface",
117
+ subtitle: "Weekly review workspace",
118
+ href: "/weekly-review"
119
+ },
120
+ movement: {
121
+ title: "Movement Surface",
122
+ subtitle: "Movement workspace",
123
+ href: "/movement"
124
+ },
125
+ sleep: {
126
+ title: "Sleep Surface",
127
+ subtitle: "Sleep workspace",
128
+ href: "/sleep"
129
+ },
130
+ sports: {
131
+ title: "Sports Surface",
132
+ subtitle: "Sports workspace",
133
+ href: "/sports"
134
+ }
135
+ };
136
+ function truncate(value, max = 160) {
137
+ const normalized = (value ?? "").replace(/\s+/g, " ").trim();
138
+ if (normalized.length <= max) {
139
+ return normalized;
140
+ }
141
+ return `${normalized.slice(0, max - 1).trimEnd()}...`;
142
+ }
143
+ function buildOwner(owner) {
144
+ if (!owner?.user) {
145
+ return null;
146
+ }
147
+ return {
148
+ userId: owner.userId ?? null,
149
+ displayName: owner.user.displayName,
150
+ accentColor: owner.user.accentColor,
151
+ kind: owner.user.kind
152
+ };
153
+ }
154
+ function makeNode(input) {
155
+ const previewStats = (input.previewStats ?? [])
156
+ .filter((stat) => stat.value !== null && stat.value !== undefined && `${stat.value}`.trim().length > 0)
157
+ .slice(0, 3)
158
+ .map((stat) => ({
159
+ label: stat.label,
160
+ value: String(stat.value)
161
+ }));
162
+ return {
163
+ id: buildKnowledgeGraphNodeId(input.entityType, input.entityId),
164
+ entityType: input.entityType,
165
+ entityId: input.entityId,
166
+ entityKind: input.entityKind,
167
+ title: truncate(input.title, 90) || input.entityId,
168
+ subtitle: truncate(input.subtitle, 120),
169
+ description: truncate(input.description, 220),
170
+ href: input.href ?? null,
171
+ graphHref: buildKnowledgeGraphFocusHref(input.entityType, input.entityId),
172
+ iconName: null,
173
+ accentToken: null,
174
+ size: BASE_NODE_SIZE[input.entityKind],
175
+ importance: BASE_NODE_SIZE[input.entityKind],
176
+ previewStats,
177
+ owner: buildOwner(input.owner),
178
+ tags: input.tags ?? [],
179
+ updatedAt: input.updatedAt ?? null,
180
+ graphStats: {
181
+ degree: 0,
182
+ structuralDegree: 0,
183
+ contextualDegree: 0,
184
+ taxonomyDegree: 0,
185
+ workspaceDegree: 0
186
+ }
187
+ };
188
+ }
189
+ function addEdge(edgeIndex, input) {
190
+ if (input.source === input.target) {
191
+ return;
192
+ }
193
+ const edgeId = [
194
+ input.relationKind,
195
+ input.source,
196
+ input.target,
197
+ input.label
198
+ ].join("|");
199
+ if (edgeIndex.has(edgeId)) {
200
+ return;
201
+ }
202
+ edgeIndex.set(edgeId, {
203
+ id: edgeId,
204
+ family: KNOWLEDGE_GRAPH_RELATION_FAMILY_MAP[input.relationKind],
205
+ ...input
206
+ });
207
+ }
208
+ function listWikiLinkRows(noteIds) {
209
+ if (noteIds.length === 0) {
210
+ return [];
211
+ }
212
+ const placeholders = noteIds.map(() => "?").join(", ");
213
+ return getDatabase()
214
+ .prepare(`SELECT source_note_id, target_type, target_note_id, target_entity_type, target_entity_id
215
+ FROM wiki_link_edges
216
+ WHERE source_note_id IN (${placeholders})`)
217
+ .all(...noteIds);
218
+ }
219
+ function buildFocusPayload(graph, focusNodeId) {
220
+ return buildKnowledgeGraphFocusPayload(graph.nodes, graph.edges, focusNodeId);
221
+ }
222
+ export function buildKnowledgeGraph(userIds, query = {}) {
223
+ const goals = filterOwnedEntities("goal", listGoals(), userIds);
224
+ const projects = listProjectSummaries({ userIds });
225
+ const tasks = filterOwnedEntities("task", listTasks(), userIds);
226
+ const tags = filterOwnedEntities("tag", listTags(), userIds);
227
+ const strategies = listStrategies({ userIds });
228
+ const habits = listHabits({ userIds });
229
+ const notes = listNotes({ userIds });
230
+ const insights = listInsights({ userIds });
231
+ const calendarEvents = listCalendarEvents({ ...GRAPH_RANGE, userIds });
232
+ const workBlocks = listWorkBlockTemplates({ userIds });
233
+ const timeboxes = listTaskTimeboxes({ ...GRAPH_RANGE, userIds });
234
+ const eventTypes = filterOwnedEntities("event_type", listEventTypes(), userIds);
235
+ const emotions = filterOwnedEntities("emotion_definition", listEmotionDefinitions(), userIds);
236
+ const values = filterOwnedEntities("psyche_value", listPsycheValues(), userIds);
237
+ const patterns = filterOwnedEntities("behavior_pattern", listBehaviorPatterns(), userIds);
238
+ const behaviors = filterOwnedEntities("behavior", listBehaviors(), userIds);
239
+ const beliefs = filterOwnedEntities("belief_entry", listBeliefEntries(), userIds);
240
+ const modes = filterOwnedEntities("mode_profile", listModeProfiles(), userIds);
241
+ const modeSessions = filterOwnedEntities("mode_guide_session", listModeGuideSessions(), userIds);
242
+ const reports = filterOwnedEntities("trigger_report", listTriggerReports(), userIds);
243
+ const wikiSpaces = listWikiSpaces();
244
+ const flows = listAiConnectors();
245
+ const nodes = new Map();
246
+ const edges = new Map();
247
+ const noteIds = notes.map((note) => note.id);
248
+ const wikiLinkRows = listWikiLinkRows(noteIds);
249
+ const noteById = new Map(notes.map((note) => [note.id, note]));
250
+ const tagById = new Map(tags.map((tag) => [tag.id, tag]));
251
+ for (const goal of goals) {
252
+ nodes.set(buildKnowledgeGraphNodeId("goal", goal.id), makeNode({
253
+ entityType: "goal",
254
+ entityId: goal.id,
255
+ entityKind: "goal",
256
+ title: goal.title,
257
+ subtitle: goal.horizon,
258
+ description: goal.description,
259
+ previewStats: [
260
+ { label: "Horizon", value: goal.horizon },
261
+ { label: "Status", value: goal.status },
262
+ { label: "Target XP", value: goal.targetPoints }
263
+ ],
264
+ owner: goal,
265
+ tags: goal.tagIds
266
+ .map((tagId) => tagById.get(tagId))
267
+ .filter(Boolean)
268
+ .map((tag) => ({ id: tag.id, label: tag.name })),
269
+ href: getKnowledgeGraphEntityHref("goal", goal.id),
270
+ updatedAt: goal.updatedAt
271
+ }));
272
+ for (const tagId of goal.tagIds) {
273
+ addEdge(edges, {
274
+ source: buildKnowledgeGraphNodeId("tag", tagId),
275
+ target: buildKnowledgeGraphNodeId("goal", goal.id),
276
+ relationKind: "tag_goal",
277
+ label: "Tags goal",
278
+ strength: 0.7,
279
+ directional: true,
280
+ structural: false
281
+ });
282
+ }
283
+ }
284
+ for (const tag of tags) {
285
+ nodes.set(buildKnowledgeGraphNodeId("tag", tag.id), makeNode({
286
+ entityType: "tag",
287
+ entityId: tag.id,
288
+ entityKind: "tag",
289
+ title: tag.name,
290
+ subtitle: tag.kind,
291
+ description: tag.description,
292
+ previewStats: [
293
+ { label: "Kind", value: tag.kind },
294
+ { label: "Color", value: tag.color }
295
+ ],
296
+ owner: tag,
297
+ href: "/tags"
298
+ }));
299
+ }
300
+ for (const project of projects) {
301
+ nodes.set(buildKnowledgeGraphNodeId("project", project.id), makeNode({
302
+ entityType: "project",
303
+ entityId: project.id,
304
+ entityKind: "project",
305
+ title: project.title,
306
+ subtitle: project.goalTitle,
307
+ description: project.description,
308
+ previewStats: [
309
+ { label: "Progress", value: `${project.progress}%` },
310
+ { label: "Tasks", value: project.totalTasks },
311
+ { label: "Status", value: project.status }
312
+ ],
313
+ owner: project,
314
+ href: getKnowledgeGraphEntityHref("project", project.id),
315
+ updatedAt: "updatedAt" in project ? project.updatedAt : null
316
+ }));
317
+ addEdge(edges, {
318
+ source: buildKnowledgeGraphNodeId("goal", project.goalId),
319
+ target: buildKnowledgeGraphNodeId("project", project.id),
320
+ relationKind: "goal_project",
321
+ label: "Supports goal",
322
+ strength: 0.96,
323
+ directional: true,
324
+ structural: true
325
+ });
326
+ }
327
+ for (const task of tasks) {
328
+ nodes.set(buildKnowledgeGraphNodeId("task", task.id), makeNode({
329
+ entityType: "task",
330
+ entityId: task.id,
331
+ entityKind: "task",
332
+ title: task.title,
333
+ subtitle: task.status.replaceAll("_", " "),
334
+ description: task.description,
335
+ previewStats: [
336
+ { label: "Status", value: task.status.replaceAll("_", " ") },
337
+ { label: "Priority", value: task.priority },
338
+ { label: "XP", value: task.points }
339
+ ],
340
+ owner: task,
341
+ tags: task.tagIds
342
+ .map((tagId) => tagById.get(tagId))
343
+ .filter(Boolean)
344
+ .map((tag) => ({ id: tag.id, label: tag.name })),
345
+ href: getKnowledgeGraphEntityHref("task", task.id),
346
+ updatedAt: task.updatedAt
347
+ }));
348
+ if (task.projectId) {
349
+ addEdge(edges, {
350
+ source: buildKnowledgeGraphNodeId("project", task.projectId),
351
+ target: buildKnowledgeGraphNodeId("task", task.id),
352
+ relationKind: "project_task",
353
+ label: "Contains task",
354
+ strength: 0.98,
355
+ directional: true,
356
+ structural: true
357
+ });
358
+ }
359
+ if (task.goalId && !task.projectId) {
360
+ addEdge(edges, {
361
+ source: buildKnowledgeGraphNodeId("goal", task.goalId),
362
+ target: buildKnowledgeGraphNodeId("task", task.id),
363
+ relationKind: "goal_task",
364
+ label: "Direct goal task",
365
+ strength: 0.8,
366
+ directional: true,
367
+ structural: false
368
+ });
369
+ }
370
+ for (const tagId of task.tagIds) {
371
+ addEdge(edges, {
372
+ source: buildKnowledgeGraphNodeId("tag", tagId),
373
+ target: buildKnowledgeGraphNodeId("task", task.id),
374
+ relationKind: "tag_task",
375
+ label: "Tags task",
376
+ strength: 0.68,
377
+ directional: true,
378
+ structural: false
379
+ });
380
+ }
381
+ }
382
+ for (const strategy of strategies) {
383
+ nodes.set(buildKnowledgeGraphNodeId("strategy", strategy.id), makeNode({
384
+ entityType: "strategy",
385
+ entityId: strategy.id,
386
+ entityKind: "strategy",
387
+ title: strategy.title,
388
+ subtitle: `${strategy.metrics.alignmentScore}% aligned`,
389
+ description: strategy.overview || strategy.endStateDescription,
390
+ previewStats: [
391
+ { label: "Aligned", value: `${strategy.metrics.alignmentScore}%` },
392
+ { label: "Nodes", value: strategy.metrics.totalNodeCount },
393
+ { label: "Status", value: strategy.status }
394
+ ],
395
+ owner: strategy,
396
+ tags: strategy.linkedEntities
397
+ .filter((link) => link.entityType === "tag")
398
+ .map((link) => tagById.get(link.entityId))
399
+ .filter(Boolean)
400
+ .map((tag) => ({ id: tag.id, label: tag.name })),
401
+ href: getKnowledgeGraphEntityHref("strategy", strategy.id),
402
+ updatedAt: strategy.updatedAt
403
+ }));
404
+ for (const goalId of strategy.targetGoalIds) {
405
+ addEdge(edges, {
406
+ source: buildKnowledgeGraphNodeId("strategy", strategy.id),
407
+ target: buildKnowledgeGraphNodeId("goal", goalId),
408
+ relationKind: "strategy_target",
409
+ label: "Targets goal",
410
+ strength: 0.92,
411
+ directional: true,
412
+ structural: true
413
+ });
414
+ }
415
+ for (const projectId of strategy.targetProjectIds) {
416
+ addEdge(edges, {
417
+ source: buildKnowledgeGraphNodeId("strategy", strategy.id),
418
+ target: buildKnowledgeGraphNodeId("project", projectId),
419
+ relationKind: "strategy_target",
420
+ label: "Targets project",
421
+ strength: 0.92,
422
+ directional: true,
423
+ structural: true
424
+ });
425
+ }
426
+ for (const link of strategy.linkedEntities) {
427
+ if (link.entityType === "tag") {
428
+ addEdge(edges, {
429
+ source: buildKnowledgeGraphNodeId("tag", link.entityId),
430
+ target: buildKnowledgeGraphNodeId("strategy", strategy.id),
431
+ relationKind: "tag_strategy",
432
+ label: "Tags strategy",
433
+ strength: 0.68,
434
+ directional: true,
435
+ structural: false
436
+ });
437
+ continue;
438
+ }
439
+ addEdge(edges, {
440
+ source: buildKnowledgeGraphNodeId("strategy", strategy.id),
441
+ target: buildKnowledgeGraphNodeId(link.entityType, link.entityId),
442
+ relationKind: "strategy_link",
443
+ label: "References entity",
444
+ strength: 0.66,
445
+ directional: true,
446
+ structural: false
447
+ });
448
+ }
449
+ for (const node of strategy.graph.nodes) {
450
+ addEdge(edges, {
451
+ source: buildKnowledgeGraphNodeId("strategy", strategy.id),
452
+ target: buildKnowledgeGraphNodeId(node.entityType, node.entityId),
453
+ relationKind: "strategy_step",
454
+ label: "Uses plan step",
455
+ strength: 0.82,
456
+ directional: true,
457
+ structural: true
458
+ });
459
+ }
460
+ }
461
+ for (const habit of habits) {
462
+ nodes.set(buildKnowledgeGraphNodeId("habit", habit.id), makeNode({
463
+ entityType: "habit",
464
+ entityId: habit.id,
465
+ entityKind: "habit",
466
+ title: habit.title,
467
+ subtitle: habit.frequency,
468
+ description: habit.description,
469
+ previewStats: [
470
+ { label: "Frequency", value: habit.frequency },
471
+ { label: "Streak", value: habit.streakCount },
472
+ { label: "Completion", value: `${Math.round(habit.completionRate)}%` }
473
+ ],
474
+ owner: habit,
475
+ href: getKnowledgeGraphEntityHref("habit", habit.id),
476
+ updatedAt: habit.updatedAt
477
+ }));
478
+ for (const linkedId of habit.linkedGoalIds) {
479
+ addEdge(edges, {
480
+ source: buildKnowledgeGraphNodeId("habit", habit.id),
481
+ target: buildKnowledgeGraphNodeId("goal", linkedId),
482
+ relationKind: "habit_link",
483
+ label: "Habit supports goal",
484
+ strength: 0.76,
485
+ directional: true,
486
+ structural: false
487
+ });
488
+ }
489
+ for (const linkedId of habit.linkedProjectIds) {
490
+ addEdge(edges, {
491
+ source: buildKnowledgeGraphNodeId("habit", habit.id),
492
+ target: buildKnowledgeGraphNodeId("project", linkedId),
493
+ relationKind: "habit_link",
494
+ label: "Habit supports project",
495
+ strength: 0.76,
496
+ directional: true,
497
+ structural: false
498
+ });
499
+ }
500
+ for (const linkedId of habit.linkedTaskIds) {
501
+ addEdge(edges, {
502
+ source: buildKnowledgeGraphNodeId("habit", habit.id),
503
+ target: buildKnowledgeGraphNodeId("task", linkedId),
504
+ relationKind: "habit_link",
505
+ label: "Habit supports task",
506
+ strength: 0.76,
507
+ directional: true,
508
+ structural: false
509
+ });
510
+ }
511
+ for (const linkedId of habit.linkedValueIds) {
512
+ addEdge(edges, {
513
+ source: buildKnowledgeGraphNodeId("habit", habit.id),
514
+ target: buildKnowledgeGraphNodeId("psyche_value", linkedId),
515
+ relationKind: "habit_link",
516
+ label: "Habit supports value",
517
+ strength: 0.74,
518
+ directional: true,
519
+ structural: false
520
+ });
521
+ }
522
+ for (const linkedId of habit.linkedPatternIds) {
523
+ addEdge(edges, {
524
+ source: buildKnowledgeGraphNodeId("habit", habit.id),
525
+ target: buildKnowledgeGraphNodeId("behavior_pattern", linkedId),
526
+ relationKind: "habit_link",
527
+ label: "Habit relates to pattern",
528
+ strength: 0.74,
529
+ directional: true,
530
+ structural: false
531
+ });
532
+ }
533
+ for (const linkedId of habit.linkedBehaviorIds) {
534
+ addEdge(edges, {
535
+ source: buildKnowledgeGraphNodeId("habit", habit.id),
536
+ target: buildKnowledgeGraphNodeId("behavior", linkedId),
537
+ relationKind: "habit_link",
538
+ label: "Habit relates to behavior",
539
+ strength: 0.74,
540
+ directional: true,
541
+ structural: false
542
+ });
543
+ }
544
+ for (const linkedId of habit.linkedBeliefIds) {
545
+ addEdge(edges, {
546
+ source: buildKnowledgeGraphNodeId("habit", habit.id),
547
+ target: buildKnowledgeGraphNodeId("belief_entry", linkedId),
548
+ relationKind: "habit_link",
549
+ label: "Habit relates to belief",
550
+ strength: 0.72,
551
+ directional: true,
552
+ structural: false
553
+ });
554
+ }
555
+ for (const linkedId of habit.linkedModeIds) {
556
+ addEdge(edges, {
557
+ source: buildKnowledgeGraphNodeId("habit", habit.id),
558
+ target: buildKnowledgeGraphNodeId("mode_profile", linkedId),
559
+ relationKind: "habit_link",
560
+ label: "Habit relates to mode",
561
+ strength: 0.72,
562
+ directional: true,
563
+ structural: false
564
+ });
565
+ }
566
+ for (const linkedId of habit.linkedReportIds) {
567
+ addEdge(edges, {
568
+ source: buildKnowledgeGraphNodeId("habit", habit.id),
569
+ target: buildKnowledgeGraphNodeId("trigger_report", linkedId),
570
+ relationKind: "habit_link",
571
+ label: "Habit relates to report",
572
+ strength: 0.72,
573
+ directional: true,
574
+ structural: false
575
+ });
576
+ }
577
+ }
578
+ for (const note of notes) {
579
+ const isWiki = note.kind === "wiki";
580
+ const noteNodeId = buildKnowledgeGraphNodeId("note", note.id);
581
+ nodes.set(noteNodeId, makeNode({
582
+ entityType: "note",
583
+ entityId: note.id,
584
+ entityKind: isWiki ? "wiki_page" : "note",
585
+ title: note.title,
586
+ subtitle: isWiki ? note.slug : note.summary,
587
+ description: note.summary || note.contentPlain,
588
+ previewStats: [
589
+ { label: "Kind", value: note.kind },
590
+ { label: "Links", value: note.links.length },
591
+ { label: "Tags", value: note.tags?.length ?? 0 }
592
+ ],
593
+ owner: note,
594
+ tags: (note.tags ?? []).map((tag) => ({
595
+ id: tag,
596
+ label: tag
597
+ })),
598
+ href: getKnowledgeGraphEntityHref("note", note.id, {
599
+ noteKind: note.kind,
600
+ noteSlug: note.slug,
601
+ noteSpaceId: note.spaceId
602
+ }),
603
+ updatedAt: note.updatedAt
604
+ }));
605
+ if (isWiki && note.spaceId) {
606
+ addEdge(edges, {
607
+ source: buildKnowledgeGraphNodeId("wiki_space", note.spaceId),
608
+ target: noteNodeId,
609
+ relationKind: "wiki_parent",
610
+ label: "Contains page",
611
+ strength: 0.72,
612
+ directional: true,
613
+ structural: true
614
+ });
615
+ }
616
+ for (const link of note.links) {
617
+ addEdge(edges, {
618
+ source: noteNodeId,
619
+ target: buildKnowledgeGraphNodeId(link.entityType, link.entityId),
620
+ relationKind: "note_link",
621
+ label: "Attached note",
622
+ strength: isWiki ? 0.74 : 0.68,
623
+ directional: true,
624
+ structural: false
625
+ });
626
+ }
627
+ }
628
+ for (const space of wikiSpaces) {
629
+ nodes.set(buildKnowledgeGraphNodeId("wiki_space", space.id), makeNode({
630
+ entityType: "wiki_space",
631
+ entityId: space.id,
632
+ entityKind: "wiki_space",
633
+ title: space.label,
634
+ subtitle: space.visibility,
635
+ description: space.description,
636
+ previewStats: [
637
+ { label: "Visibility", value: space.visibility },
638
+ { label: "Slug", value: space.slug }
639
+ ],
640
+ href: getKnowledgeGraphEntityHref("wiki_space", space.id),
641
+ updatedAt: space.updatedAt
642
+ }));
643
+ }
644
+ for (const row of wikiLinkRows) {
645
+ const sourceNote = noteById.get(row.source_note_id);
646
+ if (!sourceNote) {
647
+ continue;
648
+ }
649
+ if (row.target_type === "page" && row.target_note_id) {
650
+ addEdge(edges, {
651
+ source: buildKnowledgeGraphNodeId("note", row.source_note_id),
652
+ target: buildKnowledgeGraphNodeId("note", row.target_note_id),
653
+ relationKind: "wiki_link",
654
+ label: "Wiki link",
655
+ strength: 0.64,
656
+ directional: true,
657
+ structural: false
658
+ });
659
+ }
660
+ if (row.target_type === "entity" && row.target_entity_type && row.target_entity_id) {
661
+ addEdge(edges, {
662
+ source: buildKnowledgeGraphNodeId("note", row.source_note_id),
663
+ target: buildKnowledgeGraphNodeId(row.target_entity_type, row.target_entity_id),
664
+ relationKind: "wiki_link",
665
+ label: "Wiki entity link",
666
+ strength: 0.64,
667
+ directional: true,
668
+ structural: false
669
+ });
670
+ }
671
+ }
672
+ for (const insight of insights) {
673
+ nodes.set(buildKnowledgeGraphNodeId("insight", insight.id), makeNode({
674
+ entityType: "insight",
675
+ entityId: insight.id,
676
+ entityKind: "insight",
677
+ title: insight.title,
678
+ subtitle: insight.status,
679
+ description: insight.summary || insight.recommendation,
680
+ previewStats: [
681
+ { label: "Status", value: insight.status },
682
+ { label: "Confidence", value: `${Math.round(insight.confidence * 100)}%` },
683
+ { label: "Evidence", value: insight.evidence.length }
684
+ ],
685
+ owner: insight,
686
+ href: getKnowledgeGraphEntityHref("insight", insight.id),
687
+ updatedAt: insight.updatedAt
688
+ }));
689
+ if (insight.entityType && insight.entityId) {
690
+ addEdge(edges, {
691
+ source: buildKnowledgeGraphNodeId("insight", insight.id),
692
+ target: buildKnowledgeGraphNodeId(insight.entityType, insight.entityId),
693
+ relationKind: "note_link",
694
+ label: "Primary entity",
695
+ strength: 0.7,
696
+ directional: true,
697
+ structural: false
698
+ });
699
+ }
700
+ for (const evidence of insight.evidence) {
701
+ addEdge(edges, {
702
+ source: buildKnowledgeGraphNodeId("insight", insight.id),
703
+ target: buildKnowledgeGraphNodeId(evidence.entityType, evidence.entityId),
704
+ relationKind: "note_link",
705
+ label: "Evidence",
706
+ strength: 0.66,
707
+ directional: true,
708
+ structural: false
709
+ });
710
+ }
711
+ }
712
+ for (const event of calendarEvents) {
713
+ nodes.set(buildKnowledgeGraphNodeId("calendar_event", event.id), makeNode({
714
+ entityType: "calendar_event",
715
+ entityId: event.id,
716
+ entityKind: "calendar_event",
717
+ title: event.title,
718
+ subtitle: event.eventType || event.originType,
719
+ description: event.description || event.location,
720
+ previewStats: [
721
+ { label: "Origin", value: event.originType },
722
+ { label: "Start", value: new Date(event.startAt).toLocaleDateString() },
723
+ { label: "Links", value: event.links.length }
724
+ ],
725
+ owner: event,
726
+ href: getKnowledgeGraphEntityHref("calendar_event", event.id),
727
+ updatedAt: event.updatedAt
728
+ }));
729
+ for (const link of event.links) {
730
+ addEdge(edges, {
731
+ source: buildKnowledgeGraphNodeId("calendar_event", event.id),
732
+ target: buildKnowledgeGraphNodeId(link.entityType, link.entityId),
733
+ relationKind: "calendar_link",
734
+ label: link.relationshipType || "Calendar link",
735
+ strength: 0.7,
736
+ directional: true,
737
+ structural: false
738
+ });
739
+ }
740
+ }
741
+ for (const workBlock of workBlocks) {
742
+ nodes.set(buildKnowledgeGraphNodeId("work_block_template", workBlock.id), makeNode({
743
+ entityType: "work_block_template",
744
+ entityId: workBlock.id,
745
+ entityKind: "work_block",
746
+ title: workBlock.title,
747
+ subtitle: workBlock.kind.replaceAll("_", " "),
748
+ description: `${workBlock.blockingState} block`,
749
+ previewStats: [
750
+ { label: "Kind", value: workBlock.kind.replaceAll("_", " ") },
751
+ { label: "State", value: workBlock.blockingState },
752
+ { label: "Days", value: workBlock.weekDays.length }
753
+ ],
754
+ owner: workBlock,
755
+ href: getKnowledgeGraphEntityHref("work_block_template", workBlock.id),
756
+ updatedAt: workBlock.updatedAt
757
+ }));
758
+ }
759
+ for (const timebox of timeboxes) {
760
+ nodes.set(buildKnowledgeGraphNodeId("task_timebox", timebox.id), makeNode({
761
+ entityType: "task_timebox",
762
+ entityId: timebox.id,
763
+ entityKind: "timebox",
764
+ title: timebox.title,
765
+ subtitle: timebox.status,
766
+ description: `${timebox.source} timebox`,
767
+ previewStats: [
768
+ { label: "Status", value: timebox.status },
769
+ { label: "Source", value: timebox.source },
770
+ { label: "Starts", value: new Date(timebox.startsAt).toLocaleDateString() }
771
+ ],
772
+ owner: timebox,
773
+ href: getKnowledgeGraphEntityHref("task_timebox", timebox.id),
774
+ updatedAt: timebox.updatedAt
775
+ }));
776
+ addEdge(edges, {
777
+ source: buildKnowledgeGraphNodeId("task_timebox", timebox.id),
778
+ target: buildKnowledgeGraphNodeId("task", timebox.taskId),
779
+ relationKind: "timebox_task",
780
+ label: "Timeboxes task",
781
+ strength: 0.9,
782
+ directional: true,
783
+ structural: true
784
+ });
785
+ if (timebox.projectId) {
786
+ addEdge(edges, {
787
+ source: buildKnowledgeGraphNodeId("task_timebox", timebox.id),
788
+ target: buildKnowledgeGraphNodeId("project", timebox.projectId),
789
+ relationKind: "timebox_project",
790
+ label: "Timeboxes project",
791
+ strength: 0.78,
792
+ directional: true,
793
+ structural: false
794
+ });
795
+ }
796
+ }
797
+ for (const eventType of eventTypes) {
798
+ nodes.set(buildKnowledgeGraphNodeId("event_type", eventType.id), makeNode({
799
+ entityType: "event_type",
800
+ entityId: eventType.id,
801
+ entityKind: "event_type",
802
+ title: eventType.label,
803
+ subtitle: eventType.system ? "System type" : "Custom type",
804
+ description: eventType.description,
805
+ previewStats: [
806
+ { label: "System", value: eventType.system ? "Yes" : "No" }
807
+ ],
808
+ owner: eventType,
809
+ href: "/psyche/reports",
810
+ updatedAt: eventType.updatedAt
811
+ }));
812
+ }
813
+ for (const emotion of emotions) {
814
+ nodes.set(buildKnowledgeGraphNodeId("emotion_definition", emotion.id), makeNode({
815
+ entityType: "emotion_definition",
816
+ entityId: emotion.id,
817
+ entityKind: "emotion",
818
+ title: emotion.label,
819
+ subtitle: emotion.category,
820
+ description: emotion.description,
821
+ previewStats: [
822
+ { label: "Category", value: emotion.category },
823
+ { label: "System", value: emotion.system ? "Yes" : "No" }
824
+ ],
825
+ owner: emotion,
826
+ href: "/psyche/reports",
827
+ updatedAt: emotion.updatedAt
828
+ }));
829
+ }
830
+ for (const value of values) {
831
+ nodes.set(buildKnowledgeGraphNodeId("psyche_value", value.id), makeNode({
832
+ entityType: "psyche_value",
833
+ entityId: value.id,
834
+ entityKind: "value",
835
+ title: value.title,
836
+ subtitle: value.valuedDirection,
837
+ description: value.description || value.whyItMatters,
838
+ previewStats: [
839
+ { label: "Goals", value: value.linkedGoalIds.length },
840
+ { label: "Projects", value: value.linkedProjectIds.length },
841
+ { label: "Tasks", value: value.linkedTaskIds.length }
842
+ ],
843
+ owner: value,
844
+ href: getKnowledgeGraphEntityHref("psyche_value", value.id),
845
+ updatedAt: value.updatedAt
846
+ }));
847
+ for (const goalId of value.linkedGoalIds) {
848
+ addEdge(edges, {
849
+ source: buildKnowledgeGraphNodeId("psyche_value", value.id),
850
+ target: buildKnowledgeGraphNodeId("goal", goalId),
851
+ relationKind: "value_goal",
852
+ label: "Anchors goal",
853
+ strength: 0.72,
854
+ directional: true,
855
+ structural: false
856
+ });
857
+ }
858
+ for (const projectId of value.linkedProjectIds) {
859
+ addEdge(edges, {
860
+ source: buildKnowledgeGraphNodeId("psyche_value", value.id),
861
+ target: buildKnowledgeGraphNodeId("project", projectId),
862
+ relationKind: "value_project",
863
+ label: "Anchors project",
864
+ strength: 0.72,
865
+ directional: true,
866
+ structural: false
867
+ });
868
+ }
869
+ for (const taskId of value.linkedTaskIds) {
870
+ addEdge(edges, {
871
+ source: buildKnowledgeGraphNodeId("psyche_value", value.id),
872
+ target: buildKnowledgeGraphNodeId("task", taskId),
873
+ relationKind: "value_task",
874
+ label: "Anchors task",
875
+ strength: 0.72,
876
+ directional: true,
877
+ structural: false
878
+ });
879
+ }
880
+ }
881
+ for (const pattern of patterns) {
882
+ nodes.set(buildKnowledgeGraphNodeId("behavior_pattern", pattern.id), makeNode({
883
+ entityType: "behavior_pattern",
884
+ entityId: pattern.id,
885
+ entityKind: "pattern",
886
+ title: pattern.title,
887
+ subtitle: pattern.targetBehavior,
888
+ description: pattern.description,
889
+ previewStats: [
890
+ { label: "Values", value: pattern.linkedValueIds.length },
891
+ { label: "Beliefs", value: pattern.linkedBeliefIds.length },
892
+ { label: "Modes", value: pattern.linkedModeIds.length }
893
+ ],
894
+ owner: pattern,
895
+ href: getKnowledgeGraphEntityHref("behavior_pattern", pattern.id),
896
+ updatedAt: pattern.updatedAt
897
+ }));
898
+ for (const linkedId of pattern.linkedValueIds) {
899
+ addEdge(edges, {
900
+ source: buildKnowledgeGraphNodeId("behavior_pattern", pattern.id),
901
+ target: buildKnowledgeGraphNodeId("psyche_value", linkedId),
902
+ relationKind: "pattern_value",
903
+ label: "Pattern to value",
904
+ strength: 0.78,
905
+ directional: true,
906
+ structural: false
907
+ });
908
+ }
909
+ for (const linkedId of pattern.linkedBeliefIds) {
910
+ addEdge(edges, {
911
+ source: buildKnowledgeGraphNodeId("behavior_pattern", pattern.id),
912
+ target: buildKnowledgeGraphNodeId("belief_entry", linkedId),
913
+ relationKind: "pattern_belief",
914
+ label: "Pattern to belief",
915
+ strength: 0.76,
916
+ directional: true,
917
+ structural: false
918
+ });
919
+ }
920
+ for (const linkedId of pattern.linkedModeIds) {
921
+ addEdge(edges, {
922
+ source: buildKnowledgeGraphNodeId("behavior_pattern", pattern.id),
923
+ target: buildKnowledgeGraphNodeId("mode_profile", linkedId),
924
+ relationKind: "pattern_mode",
925
+ label: "Pattern to mode",
926
+ strength: 0.76,
927
+ directional: true,
928
+ structural: false
929
+ });
930
+ }
931
+ }
932
+ for (const behavior of behaviors) {
933
+ nodes.set(buildKnowledgeGraphNodeId("behavior", behavior.id), makeNode({
934
+ entityType: "behavior",
935
+ entityId: behavior.id,
936
+ entityKind: "behavior",
937
+ title: behavior.title,
938
+ subtitle: behavior.kind,
939
+ description: behavior.description,
940
+ previewStats: [
941
+ { label: "Patterns", value: behavior.linkedPatternIds.length },
942
+ { label: "Values", value: behavior.linkedValueIds.length },
943
+ { label: "Modes", value: behavior.linkedModeIds.length }
944
+ ],
945
+ owner: behavior,
946
+ href: getKnowledgeGraphEntityHref("behavior", behavior.id),
947
+ updatedAt: behavior.updatedAt
948
+ }));
949
+ for (const linkedId of behavior.linkedPatternIds) {
950
+ addEdge(edges, {
951
+ source: buildKnowledgeGraphNodeId("behavior", behavior.id),
952
+ target: buildKnowledgeGraphNodeId("behavior_pattern", linkedId),
953
+ relationKind: "behavior_pattern",
954
+ label: "Behavior to pattern",
955
+ strength: 0.8,
956
+ directional: true,
957
+ structural: false
958
+ });
959
+ }
960
+ for (const linkedId of behavior.linkedValueIds) {
961
+ addEdge(edges, {
962
+ source: buildKnowledgeGraphNodeId("behavior", behavior.id),
963
+ target: buildKnowledgeGraphNodeId("psyche_value", linkedId),
964
+ relationKind: "behavior_value",
965
+ label: "Behavior to value",
966
+ strength: 0.76,
967
+ directional: true,
968
+ structural: false
969
+ });
970
+ }
971
+ for (const linkedId of behavior.linkedSchemaIds) {
972
+ addEdge(edges, {
973
+ source: buildKnowledgeGraphNodeId("behavior", behavior.id),
974
+ target: buildKnowledgeGraphNodeId("belief_entry", linkedId),
975
+ relationKind: "behavior_belief",
976
+ label: "Behavior to belief",
977
+ strength: 0.74,
978
+ directional: true,
979
+ structural: false
980
+ });
981
+ }
982
+ for (const linkedId of behavior.linkedModeIds) {
983
+ addEdge(edges, {
984
+ source: buildKnowledgeGraphNodeId("behavior", behavior.id),
985
+ target: buildKnowledgeGraphNodeId("mode_profile", linkedId),
986
+ relationKind: "behavior_mode",
987
+ label: "Behavior to mode",
988
+ strength: 0.74,
989
+ directional: true,
990
+ structural: false
991
+ });
992
+ }
993
+ }
994
+ for (const belief of beliefs) {
995
+ nodes.set(buildKnowledgeGraphNodeId("belief_entry", belief.id), makeNode({
996
+ entityType: "belief_entry",
997
+ entityId: belief.id,
998
+ entityKind: "belief",
999
+ title: truncate(belief.statement, 82),
1000
+ subtitle: belief.beliefType,
1001
+ description: belief.flexibleAlternative || belief.originNote,
1002
+ previewStats: [
1003
+ { label: "Confidence", value: `${belief.confidence}%` },
1004
+ { label: "Values", value: belief.linkedValueIds.length },
1005
+ { label: "Reports", value: belief.linkedReportIds.length }
1006
+ ],
1007
+ owner: belief,
1008
+ href: getKnowledgeGraphEntityHref("belief_entry", belief.id),
1009
+ updatedAt: belief.updatedAt
1010
+ }));
1011
+ for (const linkedId of belief.linkedValueIds) {
1012
+ addEdge(edges, {
1013
+ source: buildKnowledgeGraphNodeId("belief_entry", belief.id),
1014
+ target: buildKnowledgeGraphNodeId("psyche_value", linkedId),
1015
+ relationKind: "belief_value",
1016
+ label: "Belief to value",
1017
+ strength: 0.76,
1018
+ directional: true,
1019
+ structural: false
1020
+ });
1021
+ }
1022
+ for (const linkedId of belief.linkedBehaviorIds) {
1023
+ addEdge(edges, {
1024
+ source: buildKnowledgeGraphNodeId("belief_entry", belief.id),
1025
+ target: buildKnowledgeGraphNodeId("behavior", linkedId),
1026
+ relationKind: "belief_behavior",
1027
+ label: "Belief to behavior",
1028
+ strength: 0.74,
1029
+ directional: true,
1030
+ structural: false
1031
+ });
1032
+ }
1033
+ for (const linkedId of belief.linkedModeIds) {
1034
+ addEdge(edges, {
1035
+ source: buildKnowledgeGraphNodeId("belief_entry", belief.id),
1036
+ target: buildKnowledgeGraphNodeId("mode_profile", linkedId),
1037
+ relationKind: "belief_mode",
1038
+ label: "Belief to mode",
1039
+ strength: 0.74,
1040
+ directional: true,
1041
+ structural: false
1042
+ });
1043
+ }
1044
+ for (const linkedId of belief.linkedReportIds) {
1045
+ addEdge(edges, {
1046
+ source: buildKnowledgeGraphNodeId("belief_entry", belief.id),
1047
+ target: buildKnowledgeGraphNodeId("trigger_report", linkedId),
1048
+ relationKind: "belief_report",
1049
+ label: "Belief to report",
1050
+ strength: 0.72,
1051
+ directional: true,
1052
+ structural: false
1053
+ });
1054
+ }
1055
+ }
1056
+ for (const mode of modes) {
1057
+ nodes.set(buildKnowledgeGraphNodeId("mode_profile", mode.id), makeNode({
1058
+ entityType: "mode_profile",
1059
+ entityId: mode.id,
1060
+ entityKind: "mode",
1061
+ title: mode.title,
1062
+ subtitle: mode.family.replaceAll("_", " "),
1063
+ description: mode.persona || mode.protectiveJob,
1064
+ previewStats: [
1065
+ { label: "Family", value: mode.family.replaceAll("_", " ") },
1066
+ { label: "Patterns", value: mode.linkedPatternIds.length },
1067
+ { label: "Behaviors", value: mode.linkedBehaviorIds.length }
1068
+ ],
1069
+ owner: mode,
1070
+ href: getKnowledgeGraphEntityHref("mode_profile", mode.id),
1071
+ updatedAt: mode.updatedAt
1072
+ }));
1073
+ for (const linkedId of mode.linkedPatternIds) {
1074
+ addEdge(edges, {
1075
+ source: buildKnowledgeGraphNodeId("mode_profile", mode.id),
1076
+ target: buildKnowledgeGraphNodeId("behavior_pattern", linkedId),
1077
+ relationKind: "mode_pattern",
1078
+ label: "Mode to pattern",
1079
+ strength: 0.76,
1080
+ directional: true,
1081
+ structural: false
1082
+ });
1083
+ }
1084
+ for (const linkedId of mode.linkedBehaviorIds) {
1085
+ addEdge(edges, {
1086
+ source: buildKnowledgeGraphNodeId("mode_profile", mode.id),
1087
+ target: buildKnowledgeGraphNodeId("behavior", linkedId),
1088
+ relationKind: "mode_behavior",
1089
+ label: "Mode to behavior",
1090
+ strength: 0.74,
1091
+ directional: true,
1092
+ structural: false
1093
+ });
1094
+ }
1095
+ for (const linkedId of mode.linkedValueIds) {
1096
+ addEdge(edges, {
1097
+ source: buildKnowledgeGraphNodeId("mode_profile", mode.id),
1098
+ target: buildKnowledgeGraphNodeId("psyche_value", linkedId),
1099
+ relationKind: "mode_value",
1100
+ label: "Mode to value",
1101
+ strength: 0.74,
1102
+ directional: true,
1103
+ structural: false
1104
+ });
1105
+ }
1106
+ }
1107
+ const modeByGuideKey = new Map(modes.map((mode) => [
1108
+ `${mode.family}::${mode.archetype}`.toLowerCase(),
1109
+ mode
1110
+ ]));
1111
+ for (const session of modeSessions) {
1112
+ nodes.set(buildKnowledgeGraphNodeId("mode_guide_session", session.id), makeNode({
1113
+ entityType: "mode_guide_session",
1114
+ entityId: session.id,
1115
+ entityKind: "mode_session",
1116
+ title: truncate(session.summary, 84),
1117
+ subtitle: `${session.results.length} results`,
1118
+ description: session.summary,
1119
+ previewStats: [
1120
+ { label: "Results", value: session.results.length },
1121
+ { label: "Answers", value: session.answers.length }
1122
+ ],
1123
+ owner: session,
1124
+ href: getKnowledgeGraphEntityHref("mode_guide_session", session.id),
1125
+ updatedAt: session.updatedAt
1126
+ }));
1127
+ for (const result of session.results) {
1128
+ const matchedMode = modeByGuideKey.get(`${result.family}::${result.archetype}`.toLowerCase()) ??
1129
+ modes.find((mode) => mode.family === result.family &&
1130
+ (mode.title.toLowerCase() === result.label.toLowerCase() ||
1131
+ mode.archetype.toLowerCase() === result.label.toLowerCase()));
1132
+ if (!matchedMode) {
1133
+ continue;
1134
+ }
1135
+ addEdge(edges, {
1136
+ source: buildKnowledgeGraphNodeId("mode_guide_session", session.id),
1137
+ target: buildKnowledgeGraphNodeId("mode_profile", matchedMode.id),
1138
+ relationKind: "mode_session_mode",
1139
+ label: "Session suggests mode",
1140
+ strength: Math.max(0.6, Math.min(1, result.confidence)),
1141
+ directional: true,
1142
+ structural: false
1143
+ });
1144
+ }
1145
+ }
1146
+ for (const report of reports) {
1147
+ nodes.set(buildKnowledgeGraphNodeId("trigger_report", report.id), makeNode({
1148
+ entityType: "trigger_report",
1149
+ entityId: report.id,
1150
+ entityKind: "report",
1151
+ title: report.title,
1152
+ subtitle: report.status,
1153
+ description: report.eventSituation,
1154
+ previewStats: [
1155
+ { label: "Status", value: report.status },
1156
+ { label: "Occurred", value: report.occurredAt ? new Date(report.occurredAt).toLocaleDateString() : "Draft" },
1157
+ { label: "Next moves", value: report.nextMoves.length }
1158
+ ],
1159
+ owner: report,
1160
+ href: getKnowledgeGraphEntityHref("trigger_report", report.id),
1161
+ updatedAt: report.updatedAt
1162
+ }));
1163
+ if (report.eventTypeId) {
1164
+ addEdge(edges, {
1165
+ source: buildKnowledgeGraphNodeId("trigger_report", report.id),
1166
+ target: buildKnowledgeGraphNodeId("event_type", report.eventTypeId),
1167
+ relationKind: "report_event_type",
1168
+ label: "Categorized as event type",
1169
+ strength: 0.84,
1170
+ directional: true,
1171
+ structural: false
1172
+ });
1173
+ }
1174
+ for (const emotion of report.emotions) {
1175
+ if (!emotion.emotionDefinitionId) {
1176
+ continue;
1177
+ }
1178
+ addEdge(edges, {
1179
+ source: buildKnowledgeGraphNodeId("trigger_report", report.id),
1180
+ target: buildKnowledgeGraphNodeId("emotion_definition", emotion.emotionDefinitionId),
1181
+ relationKind: "report_emotion",
1182
+ label: "Records emotion",
1183
+ strength: Math.max(0.64, Math.min(0.92, emotion.intensity / 10)),
1184
+ directional: true,
1185
+ structural: false
1186
+ });
1187
+ }
1188
+ for (const linkedId of report.linkedValueIds) {
1189
+ addEdge(edges, {
1190
+ source: buildKnowledgeGraphNodeId("trigger_report", report.id),
1191
+ target: buildKnowledgeGraphNodeId("psyche_value", linkedId),
1192
+ relationKind: "report_value",
1193
+ label: "Report to value",
1194
+ strength: 0.8,
1195
+ directional: true,
1196
+ structural: false
1197
+ });
1198
+ }
1199
+ for (const linkedId of report.linkedPatternIds) {
1200
+ addEdge(edges, {
1201
+ source: buildKnowledgeGraphNodeId("trigger_report", report.id),
1202
+ target: buildKnowledgeGraphNodeId("behavior_pattern", linkedId),
1203
+ relationKind: "report_pattern",
1204
+ label: "Report to pattern",
1205
+ strength: 0.78,
1206
+ directional: true,
1207
+ structural: false
1208
+ });
1209
+ }
1210
+ for (const linkedId of report.linkedGoalIds) {
1211
+ addEdge(edges, {
1212
+ source: buildKnowledgeGraphNodeId("trigger_report", report.id),
1213
+ target: buildKnowledgeGraphNodeId("goal", linkedId),
1214
+ relationKind: "report_goal",
1215
+ label: "Report to goal",
1216
+ strength: 0.78,
1217
+ directional: true,
1218
+ structural: false
1219
+ });
1220
+ }
1221
+ for (const linkedId of report.linkedProjectIds) {
1222
+ addEdge(edges, {
1223
+ source: buildKnowledgeGraphNodeId("trigger_report", report.id),
1224
+ target: buildKnowledgeGraphNodeId("project", linkedId),
1225
+ relationKind: "report_project",
1226
+ label: "Report to project",
1227
+ strength: 0.78,
1228
+ directional: true,
1229
+ structural: false
1230
+ });
1231
+ }
1232
+ for (const linkedId of report.linkedTaskIds) {
1233
+ addEdge(edges, {
1234
+ source: buildKnowledgeGraphNodeId("trigger_report", report.id),
1235
+ target: buildKnowledgeGraphNodeId("task", linkedId),
1236
+ relationKind: "report_task",
1237
+ label: "Report to task",
1238
+ strength: 0.78,
1239
+ directional: true,
1240
+ structural: false
1241
+ });
1242
+ }
1243
+ for (const linkedId of report.linkedBehaviorIds) {
1244
+ addEdge(edges, {
1245
+ source: buildKnowledgeGraphNodeId("trigger_report", report.id),
1246
+ target: buildKnowledgeGraphNodeId("behavior", linkedId),
1247
+ relationKind: "report_behavior",
1248
+ label: "Report to behavior",
1249
+ strength: 0.76,
1250
+ directional: true,
1251
+ structural: false
1252
+ });
1253
+ }
1254
+ for (const linkedId of report.linkedBeliefIds) {
1255
+ addEdge(edges, {
1256
+ source: buildKnowledgeGraphNodeId("trigger_report", report.id),
1257
+ target: buildKnowledgeGraphNodeId("belief_entry", linkedId),
1258
+ relationKind: "report_belief",
1259
+ label: "Report to belief",
1260
+ strength: 0.76,
1261
+ directional: true,
1262
+ structural: false
1263
+ });
1264
+ }
1265
+ for (const linkedId of report.linkedModeIds) {
1266
+ addEdge(edges, {
1267
+ source: buildKnowledgeGraphNodeId("trigger_report", report.id),
1268
+ target: buildKnowledgeGraphNodeId("mode_profile", linkedId),
1269
+ relationKind: "report_mode",
1270
+ label: "Report to mode",
1271
+ strength: 0.76,
1272
+ directional: true,
1273
+ structural: false
1274
+ });
1275
+ }
1276
+ }
1277
+ const discoveredWorkbenchSurfaceIds = new Set(["workbench"]);
1278
+ for (const flow of flows) {
1279
+ if (flow.homeSurfaceId) {
1280
+ discoveredWorkbenchSurfaceIds.add(flow.homeSurfaceId);
1281
+ }
1282
+ }
1283
+ for (const surfaceId of discoveredWorkbenchSurfaceIds) {
1284
+ const surfaceMeta = WORKBENCH_SURFACE_ROUTES[surfaceId] ?? {
1285
+ title: surfaceId.replace(/[-_]+/g, " ").replace(/\b\w/g, (value) => value.toUpperCase()),
1286
+ subtitle: "Workbench route surface",
1287
+ href: `/workbench?surface=${encodeURIComponent(surfaceId)}`
1288
+ };
1289
+ nodes.set(buildKnowledgeGraphNodeId("workbench_surface", surfaceId), makeNode({
1290
+ entityType: "workbench_surface",
1291
+ entityId: surfaceId,
1292
+ entityKind: "workbench",
1293
+ title: surfaceMeta.title,
1294
+ subtitle: surfaceMeta.subtitle,
1295
+ description: surfaceId === "workbench"
1296
+ ? "Forge's top-level graph-flow workspace for functors, chats, and published outputs."
1297
+ : `Workbench surface routed into ${surfaceMeta.href}.`,
1298
+ previewStats: surfaceId === "workbench" ? [{ label: "Flows", value: flows.length }] : [],
1299
+ href: surfaceMeta.href
1300
+ }));
1301
+ if (surfaceId !== "workbench") {
1302
+ addEdge(edges, {
1303
+ source: buildKnowledgeGraphNodeId("workbench_surface", "workbench"),
1304
+ target: buildKnowledgeGraphNodeId("workbench_surface", surfaceId),
1305
+ relationKind: "workbench_route",
1306
+ label: "Route surface",
1307
+ strength: 0.82,
1308
+ directional: true,
1309
+ structural: true
1310
+ });
1311
+ }
1312
+ }
1313
+ for (const flow of flows) {
1314
+ const entityKind = flow.kind === "chat" ? "chat" : "functor";
1315
+ nodes.set(buildKnowledgeGraphNodeId("workbench_flow", flow.id), makeNode({
1316
+ entityType: "workbench_flow",
1317
+ entityId: flow.id,
1318
+ entityKind,
1319
+ title: flow.title,
1320
+ subtitle: flow.kind,
1321
+ description: flow.description,
1322
+ previewStats: [
1323
+ { label: "Kind", value: flow.kind },
1324
+ { label: "Nodes", value: flow.graph.nodes.length },
1325
+ { label: "Public inputs", value: flow.publicInputs.length }
1326
+ ],
1327
+ href: getKnowledgeGraphEntityHref("workbench_flow", flow.id, {
1328
+ workbenchKind: flow.kind
1329
+ }),
1330
+ updatedAt: flow.updatedAt
1331
+ }));
1332
+ addEdge(edges, {
1333
+ source: buildKnowledgeGraphNodeId("workbench_surface", flow.homeSurfaceId ?? "workbench"),
1334
+ target: buildKnowledgeGraphNodeId("workbench_flow", flow.id),
1335
+ relationKind: "workbench_flow",
1336
+ label: flow.homeSurfaceId ? "Home surface flow" : "Contains flow",
1337
+ strength: 0.88,
1338
+ directional: true,
1339
+ structural: true
1340
+ });
1341
+ }
1342
+ const graphNodes = [...nodes.values()];
1343
+ const validNodeIds = new Set(graphNodes.map((node) => node.id));
1344
+ const graphEdges = [...edges.values()].filter((edge) => validNodeIds.has(edge.source) && validNodeIds.has(edge.target));
1345
+ const degreeById = new Map();
1346
+ for (const edge of graphEdges) {
1347
+ for (const nodeId of [edge.source, edge.target]) {
1348
+ const current = degreeById.get(nodeId) ?? {
1349
+ degree: 0,
1350
+ structuralDegree: 0,
1351
+ contextualDegree: 0,
1352
+ taxonomyDegree: 0,
1353
+ workspaceDegree: 0
1354
+ };
1355
+ current.degree += 1;
1356
+ if (edge.family === "structural") {
1357
+ current.structuralDegree += 1;
1358
+ }
1359
+ else if (edge.family === "contextual") {
1360
+ current.contextualDegree += 1;
1361
+ }
1362
+ else if (edge.family === "taxonomy") {
1363
+ current.taxonomyDegree += 1;
1364
+ }
1365
+ else if (edge.family === "workspace") {
1366
+ current.workspaceDegree += 1;
1367
+ }
1368
+ degreeById.set(nodeId, current);
1369
+ }
1370
+ }
1371
+ const sizedNodes = graphNodes.map((node) => {
1372
+ const degreeStats = degreeById.get(node.id) ?? {
1373
+ degree: 0,
1374
+ structuralDegree: 0,
1375
+ contextualDegree: 0,
1376
+ taxonomyDegree: 0,
1377
+ workspaceDegree: 0
1378
+ };
1379
+ const visual = getEntityVisual(node.entityKind);
1380
+ const importance = node.importance + degreeStats.degree * 1.6;
1381
+ const size = Math.min(88, node.size + degreeStats.degree * 1.35);
1382
+ return {
1383
+ ...node,
1384
+ graphHref: buildKnowledgeGraphFocusHref(node.entityType, node.entityId),
1385
+ iconName: visual.iconName,
1386
+ accentToken: visual.colorToken.cssVariable,
1387
+ graphStats: degreeStats,
1388
+ importance,
1389
+ size
1390
+ };
1391
+ });
1392
+ const kinds = sizedNodes.reduce((counts, node) => {
1393
+ counts[node.entityKind] = (counts[node.entityKind] ?? 0) + 1;
1394
+ return counts;
1395
+ }, {});
1396
+ const relationKinds = graphEdges.reduce((counts, edge) => {
1397
+ counts[edge.relationKind] = (counts[edge.relationKind] ?? 0) + 1;
1398
+ return counts;
1399
+ }, {});
1400
+ const baseGraph = {
1401
+ generatedAt: new Date().toISOString(),
1402
+ nodes: sizedNodes,
1403
+ edges: graphEdges,
1404
+ facets: buildKnowledgeGraphFacets(sizedNodes, graphEdges),
1405
+ counts: {
1406
+ nodeCount: sizedNodes.length,
1407
+ edgeCount: graphEdges.length,
1408
+ totalNodeCount: sizedNodes.length,
1409
+ totalEdgeCount: graphEdges.length,
1410
+ filteredNodeCount: sizedNodes.length,
1411
+ filteredEdgeCount: graphEdges.length,
1412
+ kinds,
1413
+ relationKinds,
1414
+ limited: false
1415
+ }
1416
+ };
1417
+ const filteredGraph = filterKnowledgeGraphData(baseGraph, query);
1418
+ const visibleNodeIds = selectKnowledgeGraphVisibleNodeIds({
1419
+ nodes: filteredGraph.nodes,
1420
+ edges: filteredGraph.edges,
1421
+ limit: query.limit,
1422
+ focusNodeId: query.focusNodeId
1423
+ });
1424
+ const visibleNodes = filteredGraph.nodes.filter((node) => visibleNodeIds.has(node.id));
1425
+ const visibleEdges = filteredGraph.edges.filter((edge) => visibleNodeIds.has(edge.source) && visibleNodeIds.has(edge.target));
1426
+ const visibleKinds = visibleNodes.reduce((counts, node) => {
1427
+ counts[node.entityKind] = (counts[node.entityKind] ?? 0) + 1;
1428
+ return counts;
1429
+ }, {});
1430
+ const visibleRelationKinds = visibleEdges.reduce((counts, edge) => {
1431
+ counts[edge.relationKind] = (counts[edge.relationKind] ?? 0) + 1;
1432
+ return counts;
1433
+ }, {});
1434
+ return {
1435
+ generatedAt: new Date().toISOString(),
1436
+ nodes: visibleNodes,
1437
+ edges: visibleEdges,
1438
+ facets: buildKnowledgeGraphFacets(filteredGraph.nodes, filteredGraph.edges),
1439
+ counts: {
1440
+ nodeCount: visibleNodes.length,
1441
+ edgeCount: visibleEdges.length,
1442
+ totalNodeCount: baseGraph.counts.totalNodeCount,
1443
+ totalEdgeCount: baseGraph.counts.totalEdgeCount,
1444
+ filteredNodeCount: filteredGraph.nodes.length,
1445
+ filteredEdgeCount: filteredGraph.edges.length,
1446
+ kinds: visibleKinds,
1447
+ relationKinds: visibleRelationKinds,
1448
+ limited: visibleNodes.length < filteredGraph.nodes.length
1449
+ }
1450
+ };
1451
+ }
1452
+ export function buildKnowledgeGraphFocus(entityType, entityId, userIds) {
1453
+ const graph = buildKnowledgeGraph(userIds);
1454
+ return buildFocusPayload(graph, buildKnowledgeGraphNodeId(entityType, entityId));
1455
+ }