forge-openclaw-plugin 0.2.26 → 0.2.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +59 -3
- package/dist/assets/{board-ta0rUHOf.js → board-C6jCchjI.js} +2 -2
- package/dist/assets/{board-ta0rUHOf.js.map → board-C6jCchjI.js.map} +1 -1
- package/dist/assets/index-DVvS8iiU.css +1 -0
- package/dist/assets/index-zYB-9Dfo.js +85 -0
- package/dist/assets/index-zYB-9Dfo.js.map +1 -0
- package/dist/assets/knowledge-graph-layout.worker-DRvzPxhP.js +2 -0
- package/dist/assets/knowledge-graph-layout.worker-DRvzPxhP.js.map +1 -0
- package/dist/assets/{motion-fBKPB6yw.js → motion-DFHrH2rd.js} +2 -2
- package/dist/assets/{motion-fBKPB6yw.js.map → motion-DFHrH2rd.js.map} +1 -1
- package/dist/assets/{table-C-IGTQni.js → table-ZL7Di_u3.js} +2 -2
- package/dist/assets/{table-C-IGTQni.js.map → table-ZL7Di_u3.js.map} +1 -1
- package/dist/assets/{ui-DInOpaYF.js → ui-CKNPpz7q.js} +2 -2
- package/dist/assets/{ui-DInOpaYF.js.map → ui-CKNPpz7q.js.map} +1 -1
- package/dist/assets/vendor-DoNZuFhn.js +1247 -0
- package/dist/assets/vendor-DoNZuFhn.js.map +1 -0
- package/dist/index.html +7 -7
- package/dist/openclaw/local-runtime.js +16 -0
- package/dist/openclaw/routes.d.ts +27 -0
- package/dist/openclaw/routes.js +16 -12
- package/dist/server/server/migrations/037_workbench_public_inputs_and_run_inputs.sql +5 -0
- package/dist/server/server/migrations/038_data_management_settings.sql +11 -0
- package/dist/server/server/migrations/039_life_force_and_action_points.sql +114 -0
- package/dist/server/server/migrations/040_screen_time_domain.sql +89 -0
- package/dist/server/server/migrations/041_companion_source_states.sql +21 -0
- package/dist/server/server/migrations/042_movement_boxes.sql +47 -0
- package/dist/server/server/migrations/043_movement_box_overlap_overrides.sql +26 -0
- package/dist/server/server/src/app.js +1684 -117
- package/dist/server/server/src/connectors/box-registry.js +44 -9
- package/dist/server/server/src/data-management-types.js +107 -0
- package/dist/server/server/src/db.js +68 -4
- package/dist/server/server/src/demo-data.js +2 -2
- package/dist/server/server/src/health.js +702 -18
- package/dist/server/server/src/managers/platform/llm-manager.js +7 -4
- package/dist/server/server/src/managers/platform/mock-workbench-provider.js +149 -0
- package/dist/server/server/src/managers/platform/secrets-manager.js +18 -1
- package/dist/server/server/src/managers/runtime.js +9 -0
- package/dist/server/server/src/movement.js +1971 -112
- package/dist/server/server/src/openapi.js +489 -1
- package/dist/server/server/src/psyche-types.js +9 -1
- package/dist/server/server/src/repositories/activity-events.js +8 -0
- package/dist/server/server/src/repositories/ai-connectors.js +522 -74
- package/dist/server/server/src/repositories/habits.js +37 -1
- package/dist/server/server/src/repositories/model-settings.js +13 -3
- package/dist/server/server/src/repositories/notes.js +3 -0
- package/dist/server/server/src/repositories/settings.js +380 -18
- package/dist/server/server/src/repositories/tasks.js +170 -10
- package/dist/server/server/src/runtime-data-root.js +82 -0
- package/dist/server/server/src/screen-time.js +802 -0
- package/dist/server/server/src/services/data-management.js +788 -0
- package/dist/server/server/src/services/entity-crud.js +205 -2
- package/dist/server/server/src/services/knowledge-graph.js +1455 -0
- package/dist/server/server/src/services/life-force-model.js +197 -0
- package/dist/server/server/src/services/life-force.js +1270 -0
- package/dist/server/server/src/services/psyche-observation-calendar.js +383 -16
- package/dist/server/server/src/types.js +286 -13
- package/dist/server/server/src/web.js +228 -13
- package/dist/server/src/components/customization/utility-widgets.js +136 -27
- package/dist/server/src/components/ui/info-tooltip.js +25 -0
- package/dist/server/src/components/workbench-boxes/calendar/calendar-boxes.js +78 -0
- package/dist/server/src/components/workbench-boxes/goals/goals-boxes.js +62 -0
- package/dist/server/src/components/workbench-boxes/habits/habits-boxes.js +62 -0
- package/dist/server/src/components/workbench-boxes/health/health-boxes.js +63 -8
- package/dist/server/src/components/workbench-boxes/insights/insights-boxes.js +50 -0
- package/dist/server/src/components/workbench-boxes/kanban/kanban-boxes.js +62 -54
- package/dist/server/src/components/workbench-boxes/movement/movement-boxes.js +18 -8
- package/dist/server/src/components/workbench-boxes/notes/notes-boxes.js +56 -38
- package/dist/server/src/components/workbench-boxes/overview/overview-boxes.js +65 -0
- package/dist/server/src/components/workbench-boxes/preferences/preferences-boxes.js +78 -0
- package/dist/server/src/components/workbench-boxes/projects/projects-boxes.js +35 -30
- package/dist/server/src/components/workbench-boxes/psyche/psyche-boxes.js +88 -0
- package/dist/server/src/components/workbench-boxes/questionnaires/questionnaires-boxes.js +61 -0
- package/dist/server/src/components/workbench-boxes/review/review-boxes.js +53 -0
- package/dist/server/src/components/workbench-boxes/shared/define-workbench-box.js +3 -1
- package/dist/server/src/components/workbench-boxes/shared/generic-node-view.js +39 -3
- package/dist/server/src/components/workbench-boxes/strategies/strategies-boxes.js +62 -0
- package/dist/server/src/components/workbench-boxes/tasks/tasks-boxes.js +76 -0
- package/dist/server/src/components/workbench-boxes/today/today-boxes.js +47 -32
- package/dist/server/src/components/workbench-boxes/wiki/wiki-boxes.js +60 -0
- package/dist/server/src/lib/api.js +280 -21
- package/dist/server/src/lib/data-management-types.js +1 -0
- package/dist/server/src/lib/entity-visuals.js +279 -0
- package/dist/server/src/lib/knowledge-graph-types.js +276 -0
- package/dist/server/src/lib/knowledge-graph.js +470 -0
- package/dist/server/src/lib/schemas.js +4 -0
- package/dist/server/src/lib/snapshot-normalizer.js +43 -1
- package/dist/server/src/lib/workbench/contracts.js +229 -0
- package/dist/server/src/lib/workbench/nodes.js +200 -0
- package/dist/server/src/lib/workbench/registry.js +52 -5
- package/dist/server/src/lib/workbench/runtime.js +254 -38
- package/dist/server/src/lib/workbench/tool-catalog.js +68 -0
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/server/migrations/037_workbench_public_inputs_and_run_inputs.sql +5 -0
- package/server/migrations/038_data_management_settings.sql +11 -0
- package/server/migrations/039_life_force_and_action_points.sql +114 -0
- package/server/migrations/040_screen_time_domain.sql +89 -0
- package/server/migrations/041_companion_source_states.sql +21 -0
- package/server/migrations/042_movement_boxes.sql +47 -0
- package/server/migrations/043_movement_box_overlap_overrides.sql +26 -0
- package/skills/forge-openclaw/SKILL.md +24 -11
- package/skills/forge-openclaw/entity_conversation_playbooks.md +210 -34
- package/skills/forge-openclaw/psyche_entity_playbooks.md +113 -17
- package/dist/assets/index-Ro0ZF_az.css +0 -1
- package/dist/assets/index-ytlpSj23.js +0 -79
- package/dist/assets/index-ytlpSj23.js.map +0 -1
- package/dist/assets/vendor-lE3tZJcC.js +0 -876
- package/dist/assets/vendor-lE3tZJcC.js.map +0 -1
|
@@ -0,0 +1,470 @@
|
|
|
1
|
+
import { getEntityVisual, isEntityKind } from "@/lib/entity-visuals";
|
|
2
|
+
import { KNOWLEDGE_GRAPH_HIERARCHY_LANES, KNOWLEDGE_GRAPH_HIERARCHY_ORDER, KNOWLEDGE_GRAPH_RELATION_FAMILY_LABELS, KNOWLEDGE_GRAPH_RELATION_LABELS, buildKnowledgeGraphNodeId } from "@/lib/knowledge-graph-types";
|
|
3
|
+
export function getKnowledgeGraphNodeVisual(node) {
|
|
4
|
+
const kind = isEntityKind(node.entityKind) ? node.entityKind : "note";
|
|
5
|
+
return getEntityVisual(kind);
|
|
6
|
+
}
|
|
7
|
+
export function getKnowledgeGraphNodeLayer(kind) {
|
|
8
|
+
const index = KNOWLEDGE_GRAPH_HIERARCHY_ORDER.indexOf(kind);
|
|
9
|
+
return index >= 0 ? index : KNOWLEDGE_GRAPH_HIERARCHY_ORDER.length - 1;
|
|
10
|
+
}
|
|
11
|
+
export function getKnowledgeGraphNodeLane(kind) {
|
|
12
|
+
const lane = KNOWLEDGE_GRAPH_HIERARCHY_LANES.find((entry) => entry.kinds.some((entryKind) => entryKind === kind)) ??
|
|
13
|
+
KNOWLEDGE_GRAPH_HIERARCHY_LANES[KNOWLEDGE_GRAPH_HIERARCHY_LANES.length - 1];
|
|
14
|
+
return {
|
|
15
|
+
laneId: lane.id,
|
|
16
|
+
laneLabel: lane.label,
|
|
17
|
+
laneIndex: KNOWLEDGE_GRAPH_HIERARCHY_LANES.findIndex((entry) => entry.id === lane.id)
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
function initializeFamilyCounts() {
|
|
21
|
+
return {
|
|
22
|
+
structural: 0,
|
|
23
|
+
contextual: 0,
|
|
24
|
+
taxonomy: 0,
|
|
25
|
+
workspace: 0
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function buildNeighborMap(edges) {
|
|
29
|
+
const neighborMap = new Map();
|
|
30
|
+
for (const edge of edges) {
|
|
31
|
+
const sourcePeers = neighborMap.get(edge.source) ?? new Set();
|
|
32
|
+
sourcePeers.add(edge.target);
|
|
33
|
+
neighborMap.set(edge.source, sourcePeers);
|
|
34
|
+
const targetPeers = neighborMap.get(edge.target) ?? new Set();
|
|
35
|
+
targetPeers.add(edge.source);
|
|
36
|
+
neighborMap.set(edge.target, targetPeers);
|
|
37
|
+
}
|
|
38
|
+
return neighborMap;
|
|
39
|
+
}
|
|
40
|
+
function normalizeFilterText(value) {
|
|
41
|
+
return (value ?? "").trim().toLowerCase();
|
|
42
|
+
}
|
|
43
|
+
function getNodeUpdatedAtValue(node) {
|
|
44
|
+
if (!node.updatedAt) {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
const time = Date.parse(node.updatedAt);
|
|
48
|
+
return Number.isFinite(time) ? time : null;
|
|
49
|
+
}
|
|
50
|
+
export function compareKnowledgeGraphNodes(left, right) {
|
|
51
|
+
return (right.importance - left.importance ||
|
|
52
|
+
right.size - left.size ||
|
|
53
|
+
left.title.localeCompare(right.title) ||
|
|
54
|
+
left.id.localeCompare(right.id));
|
|
55
|
+
}
|
|
56
|
+
function compareKnowledgeGraphEdges(left, right) {
|
|
57
|
+
return (right.strength - left.strength ||
|
|
58
|
+
left.relationKind.localeCompare(right.relationKind) ||
|
|
59
|
+
left.label.localeCompare(right.label) ||
|
|
60
|
+
left.id.localeCompare(right.id));
|
|
61
|
+
}
|
|
62
|
+
export function buildRenderedKnowledgeGraphEdges(edges) {
|
|
63
|
+
const edgesByPair = new Map();
|
|
64
|
+
for (const edge of edges) {
|
|
65
|
+
const pairKey = `${edge.source}→${edge.target}`;
|
|
66
|
+
const current = edgesByPair.get(pairKey) ?? [];
|
|
67
|
+
current.push(edge);
|
|
68
|
+
edgesByPair.set(pairKey, current);
|
|
69
|
+
}
|
|
70
|
+
return Array.from(edgesByPair.entries())
|
|
71
|
+
.map(([pairKey, pairEdges]) => {
|
|
72
|
+
const sortedEdges = [...pairEdges].sort(compareKnowledgeGraphEdges);
|
|
73
|
+
const representative = sortedEdges[0];
|
|
74
|
+
const parallelCount = sortedEdges.length;
|
|
75
|
+
const strength = Math.max(...sortedEdges.map((edge) => edge.strength)) *
|
|
76
|
+
Math.min(1.45, 1 + (parallelCount - 1) * 0.12);
|
|
77
|
+
return {
|
|
78
|
+
id: `${pairKey}#${representative.id}`,
|
|
79
|
+
source: representative.source,
|
|
80
|
+
target: representative.target,
|
|
81
|
+
relationKind: representative.relationKind,
|
|
82
|
+
family: representative.family,
|
|
83
|
+
label: parallelCount > 1
|
|
84
|
+
? `${representative.label} +${parallelCount - 1}`
|
|
85
|
+
: representative.label,
|
|
86
|
+
strength,
|
|
87
|
+
directional: representative.directional,
|
|
88
|
+
structural: representative.structural,
|
|
89
|
+
parallelCount,
|
|
90
|
+
data: sortedEdges
|
|
91
|
+
};
|
|
92
|
+
})
|
|
93
|
+
.sort((left, right) => left.source.localeCompare(right.source) ||
|
|
94
|
+
left.target.localeCompare(right.target) ||
|
|
95
|
+
left.id.localeCompare(right.id));
|
|
96
|
+
}
|
|
97
|
+
export function buildKnowledgeGraphDatasetSignature(nodes, edges) {
|
|
98
|
+
const renderedEdges = buildRenderedKnowledgeGraphEdges(edges);
|
|
99
|
+
const nodeSignature = [...nodes]
|
|
100
|
+
.sort((left, right) => left.id.localeCompare(right.id) ||
|
|
101
|
+
left.updatedAt?.localeCompare(right.updatedAt ?? "") ||
|
|
102
|
+
0)
|
|
103
|
+
.map((node) => `${node.id}:${node.title}:${node.size}:${node.importance}:${node.updatedAt ?? ""}`)
|
|
104
|
+
.join("|");
|
|
105
|
+
const edgeSignature = renderedEdges
|
|
106
|
+
.map((edge) => `${edge.source}:${edge.target}:${edge.parallelCount}:${edge.label}:${edge.strength.toFixed(3)}`)
|
|
107
|
+
.join("|");
|
|
108
|
+
return `${nodes.length}/${renderedEdges.length}/${nodeSignature}::${edgeSignature}`;
|
|
109
|
+
}
|
|
110
|
+
export function filterKnowledgeGraphData(graph, query) {
|
|
111
|
+
const normalizedQuery = normalizeFilterText(query.q);
|
|
112
|
+
const selectedKinds = new Set(query.entityKinds ?? []);
|
|
113
|
+
const selectedTags = new Set(query.tags ?? []);
|
|
114
|
+
const selectedOwners = new Set(query.owners ?? []);
|
|
115
|
+
const selectedRelations = new Set(query.relationKinds ?? []);
|
|
116
|
+
const updatedFrom = query.updatedFrom ? Date.parse(query.updatedFrom) : null;
|
|
117
|
+
const updatedTo = query.updatedTo ? Date.parse(query.updatedTo) : null;
|
|
118
|
+
const filteredNodes = graph.nodes.filter((node) => {
|
|
119
|
+
if (selectedKinds.size > 0 && !selectedKinds.has(node.entityKind)) {
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
if (selectedTags.size > 0 &&
|
|
123
|
+
!node.tags.some((tag) => selectedTags.has(tag.id))) {
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
if (selectedOwners.size > 0) {
|
|
127
|
+
const ownerId = node.owner?.userId;
|
|
128
|
+
if (!ownerId || !selectedOwners.has(ownerId)) {
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
const updatedAtValue = getNodeUpdatedAtValue(node);
|
|
133
|
+
if (updatedFrom !== null) {
|
|
134
|
+
if (updatedAtValue === null || updatedAtValue < updatedFrom) {
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
if (updatedTo !== null) {
|
|
139
|
+
if (updatedAtValue === null || updatedAtValue > updatedTo) {
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
if (!normalizedQuery) {
|
|
144
|
+
return true;
|
|
145
|
+
}
|
|
146
|
+
const haystack = [
|
|
147
|
+
node.title,
|
|
148
|
+
node.subtitle,
|
|
149
|
+
node.description,
|
|
150
|
+
node.owner?.displayName ?? "",
|
|
151
|
+
...node.tags.map((tag) => tag.label)
|
|
152
|
+
]
|
|
153
|
+
.join(" ")
|
|
154
|
+
.toLowerCase();
|
|
155
|
+
return haystack.includes(normalizedQuery);
|
|
156
|
+
});
|
|
157
|
+
const filteredNodeIds = new Set(filteredNodes.map((node) => node.id));
|
|
158
|
+
const candidateEdges = graph.edges.filter((edge) => {
|
|
159
|
+
if (!filteredNodeIds.has(edge.source) || !filteredNodeIds.has(edge.target)) {
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
if (selectedRelations.size > 0 &&
|
|
163
|
+
!selectedRelations.has(edge.relationKind)) {
|
|
164
|
+
return false;
|
|
165
|
+
}
|
|
166
|
+
return true;
|
|
167
|
+
});
|
|
168
|
+
if (selectedRelations.size === 0) {
|
|
169
|
+
return {
|
|
170
|
+
nodes: filteredNodes,
|
|
171
|
+
edges: candidateEdges
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
const connectedNodeIds = new Set();
|
|
175
|
+
for (const edge of candidateEdges) {
|
|
176
|
+
connectedNodeIds.add(edge.source);
|
|
177
|
+
connectedNodeIds.add(edge.target);
|
|
178
|
+
}
|
|
179
|
+
return {
|
|
180
|
+
nodes: filteredNodes.filter((node) => connectedNodeIds.has(node.id)),
|
|
181
|
+
edges: candidateEdges
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
export function selectKnowledgeGraphVisibleNodeIds({ nodes, edges, limit, focusNodeId }) {
|
|
185
|
+
if (!limit || limit <= 0 || nodes.length <= limit) {
|
|
186
|
+
return new Set(nodes.map((node) => node.id));
|
|
187
|
+
}
|
|
188
|
+
const nodeMap = new Map(nodes.map((node) => [node.id, node]));
|
|
189
|
+
if (focusNodeId && nodeMap.has(focusNodeId)) {
|
|
190
|
+
const neighborMap = buildNeighborMap(edges);
|
|
191
|
+
const visibleIds = new Set();
|
|
192
|
+
const visitedIds = new Set();
|
|
193
|
+
let frontier = [focusNodeId];
|
|
194
|
+
while (frontier.length > 0 && visibleIds.size < limit) {
|
|
195
|
+
const hopNodes = frontier
|
|
196
|
+
.map((nodeId) => nodeMap.get(nodeId) ?? null)
|
|
197
|
+
.filter((node) => Boolean(node))
|
|
198
|
+
.sort(compareKnowledgeGraphNodes);
|
|
199
|
+
for (const node of hopNodes) {
|
|
200
|
+
if (visibleIds.size >= limit) {
|
|
201
|
+
break;
|
|
202
|
+
}
|
|
203
|
+
visibleIds.add(node.id);
|
|
204
|
+
}
|
|
205
|
+
const nextFrontier = new Set();
|
|
206
|
+
for (const nodeId of frontier) {
|
|
207
|
+
visitedIds.add(nodeId);
|
|
208
|
+
for (const neighborId of neighborMap.get(nodeId) ?? []) {
|
|
209
|
+
if (visitedIds.has(neighborId) ||
|
|
210
|
+
visibleIds.has(neighborId) ||
|
|
211
|
+
!nodeMap.has(neighborId)) {
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
nextFrontier.add(neighborId);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
frontier = Array.from(nextFrontier);
|
|
218
|
+
}
|
|
219
|
+
if (visibleIds.size < limit) {
|
|
220
|
+
for (const node of [...nodes].sort(compareKnowledgeGraphNodes)) {
|
|
221
|
+
if (visibleIds.has(node.id)) {
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
visibleIds.add(node.id);
|
|
225
|
+
if (visibleIds.size >= limit) {
|
|
226
|
+
break;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return visibleIds;
|
|
231
|
+
}
|
|
232
|
+
return new Set([...nodes]
|
|
233
|
+
.sort(compareKnowledgeGraphNodes)
|
|
234
|
+
.slice(0, limit)
|
|
235
|
+
.map((node) => node.id));
|
|
236
|
+
}
|
|
237
|
+
export function buildKnowledgeGraphFacets(nodes, edges) {
|
|
238
|
+
const entityKinds = nodes.reduce((counts, node) => {
|
|
239
|
+
counts[node.entityKind] = (counts[node.entityKind] ?? 0) + 1;
|
|
240
|
+
return counts;
|
|
241
|
+
}, {});
|
|
242
|
+
const relationKinds = edges.reduce((counts, edge) => {
|
|
243
|
+
counts[edge.relationKind] = (counts[edge.relationKind] ?? 0) + 1;
|
|
244
|
+
return counts;
|
|
245
|
+
}, {});
|
|
246
|
+
const tags = new Map();
|
|
247
|
+
const owners = new Map();
|
|
248
|
+
let minUpdatedAt = null;
|
|
249
|
+
let maxUpdatedAt = null;
|
|
250
|
+
for (const node of nodes) {
|
|
251
|
+
for (const tag of node.tags) {
|
|
252
|
+
const current = tags.get(tag.id) ?? { ...tag, count: 0 };
|
|
253
|
+
current.count += 1;
|
|
254
|
+
tags.set(tag.id, current);
|
|
255
|
+
}
|
|
256
|
+
if (node.owner?.userId && node.owner.displayName) {
|
|
257
|
+
const current = owners.get(node.owner.userId) ?? {
|
|
258
|
+
userId: node.owner.userId,
|
|
259
|
+
displayName: node.owner.displayName,
|
|
260
|
+
accentColor: node.owner.accentColor,
|
|
261
|
+
kind: node.owner.kind,
|
|
262
|
+
count: 0
|
|
263
|
+
};
|
|
264
|
+
current.count += 1;
|
|
265
|
+
owners.set(node.owner.userId, current);
|
|
266
|
+
}
|
|
267
|
+
if (node.updatedAt) {
|
|
268
|
+
if (!minUpdatedAt || node.updatedAt < minUpdatedAt) {
|
|
269
|
+
minUpdatedAt = node.updatedAt;
|
|
270
|
+
}
|
|
271
|
+
if (!maxUpdatedAt || node.updatedAt > maxUpdatedAt) {
|
|
272
|
+
maxUpdatedAt = node.updatedAt;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
return {
|
|
277
|
+
entityKinds: Object.entries(entityKinds)
|
|
278
|
+
.map(([value, count]) => ({
|
|
279
|
+
value: value,
|
|
280
|
+
label: getEntityVisual(value).label,
|
|
281
|
+
count
|
|
282
|
+
}))
|
|
283
|
+
.sort((left, right) => right.count - left.count || left.label.localeCompare(right.label)),
|
|
284
|
+
relationKinds: Object.entries(relationKinds)
|
|
285
|
+
.map(([value, count]) => ({
|
|
286
|
+
value: value,
|
|
287
|
+
label: KNOWLEDGE_GRAPH_RELATION_LABELS[value],
|
|
288
|
+
count
|
|
289
|
+
}))
|
|
290
|
+
.sort((left, right) => right.count - left.count || left.label.localeCompare(right.label)),
|
|
291
|
+
tags: Array.from(tags.values()).sort((left, right) => right.count - left.count || left.label.localeCompare(right.label)),
|
|
292
|
+
owners: Array.from(owners.values()).sort((left, right) => right.count - left.count || left.displayName.localeCompare(right.displayName)),
|
|
293
|
+
updatedAt: {
|
|
294
|
+
min: minUpdatedAt,
|
|
295
|
+
max: maxUpdatedAt
|
|
296
|
+
}
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
export function buildKnowledgeGraphHierarchy(nodes, edges) {
|
|
300
|
+
const sortedNodes = [...nodes].sort((left, right) => {
|
|
301
|
+
const layerDelta = getKnowledgeGraphNodeLayer(left.entityKind) -
|
|
302
|
+
getKnowledgeGraphNodeLayer(right.entityKind);
|
|
303
|
+
if (layerDelta !== 0) {
|
|
304
|
+
return layerDelta;
|
|
305
|
+
}
|
|
306
|
+
return (right.importance - left.importance || left.title.localeCompare(right.title));
|
|
307
|
+
});
|
|
308
|
+
const rowsByLayer = new Map();
|
|
309
|
+
const hierarchyNodes = sortedNodes.map((node) => {
|
|
310
|
+
const { laneId, laneIndex, laneLabel } = getKnowledgeGraphNodeLane(node.entityKind);
|
|
311
|
+
const row = rowsByLayer.get(laneIndex) ?? 0;
|
|
312
|
+
rowsByLayer.set(laneIndex, row + 1);
|
|
313
|
+
return {
|
|
314
|
+
...node,
|
|
315
|
+
layer: laneIndex,
|
|
316
|
+
laneId,
|
|
317
|
+
laneLabel,
|
|
318
|
+
row
|
|
319
|
+
};
|
|
320
|
+
});
|
|
321
|
+
const nodeMap = new Map(hierarchyNodes.map((node) => [node.id, node]));
|
|
322
|
+
const hierarchyEdges = edges.map((edge) => {
|
|
323
|
+
const sourceNode = nodeMap.get(edge.source);
|
|
324
|
+
const targetNode = nodeMap.get(edge.target);
|
|
325
|
+
const sourceLayer = sourceNode?.layer ?? 0;
|
|
326
|
+
const targetLayer = targetNode?.layer ?? 0;
|
|
327
|
+
return {
|
|
328
|
+
...edge,
|
|
329
|
+
secondary: targetLayer <= sourceLayer ||
|
|
330
|
+
(edge.family !== "structural" && edge.family !== "taxonomy")
|
|
331
|
+
};
|
|
332
|
+
});
|
|
333
|
+
return {
|
|
334
|
+
nodes: hierarchyNodes,
|
|
335
|
+
edges: hierarchyEdges
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
export function buildKnowledgeGraphFocusPayload(nodes, edges, focusNodeId) {
|
|
339
|
+
const nodeMap = new Map(nodes.map((node) => [node.id, node]));
|
|
340
|
+
const focusNode = focusNodeId ? nodeMap.get(focusNodeId) ?? null : null;
|
|
341
|
+
if (!focusNode) {
|
|
342
|
+
return {
|
|
343
|
+
generatedAt: new Date().toISOString(),
|
|
344
|
+
focusNode: null,
|
|
345
|
+
firstRingNodes: [],
|
|
346
|
+
neighborhoodEdges: [],
|
|
347
|
+
familyGroups: [],
|
|
348
|
+
relationCounts: initializeFamilyCounts(),
|
|
349
|
+
secondRingCounts: initializeFamilyCounts()
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
const neighborhoodEdges = edges.filter((edge) => edge.source === focusNode.id || edge.target === focusNode.id);
|
|
353
|
+
const neighborIds = Array.from(new Set(neighborhoodEdges.flatMap((edge) => edge.source === focusNode.id ? [edge.target] : [edge.source])));
|
|
354
|
+
const firstRingNodes = neighborIds
|
|
355
|
+
.map((nodeId) => nodeMap.get(nodeId) ?? null)
|
|
356
|
+
.filter((node) => Boolean(node));
|
|
357
|
+
const groupsByRelation = new Map();
|
|
358
|
+
const relationCounts = initializeFamilyCounts();
|
|
359
|
+
const secondRingCounts = initializeFamilyCounts();
|
|
360
|
+
const allNeighbors = buildNeighborMap(edges);
|
|
361
|
+
for (const nodeId of neighborIds) {
|
|
362
|
+
const secondRingPeers = allNeighbors.get(nodeId) ?? new Set();
|
|
363
|
+
for (const peerId of secondRingPeers) {
|
|
364
|
+
if (peerId === focusNode.id || neighborIds.includes(peerId)) {
|
|
365
|
+
continue;
|
|
366
|
+
}
|
|
367
|
+
const peerEdges = edges.filter((edge) => (edge.source === nodeId && edge.target === peerId) ||
|
|
368
|
+
(edge.target === nodeId && edge.source === peerId));
|
|
369
|
+
for (const edge of peerEdges) {
|
|
370
|
+
secondRingCounts[edge.family] += 1;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
for (const edge of neighborhoodEdges) {
|
|
375
|
+
const peerId = edge.source === focusNode.id ? edge.target : edge.source;
|
|
376
|
+
const peer = nodeMap.get(peerId);
|
|
377
|
+
if (!peer) {
|
|
378
|
+
continue;
|
|
379
|
+
}
|
|
380
|
+
relationCounts[edge.family] += 1;
|
|
381
|
+
const existing = groupsByRelation.get(edge.relationKind);
|
|
382
|
+
if (existing) {
|
|
383
|
+
if (!existing.items.some((item) => item.id === peer.id)) {
|
|
384
|
+
existing.items.push(peer);
|
|
385
|
+
}
|
|
386
|
+
continue;
|
|
387
|
+
}
|
|
388
|
+
groupsByRelation.set(edge.relationKind, {
|
|
389
|
+
relationKind: edge.relationKind,
|
|
390
|
+
family: edge.family,
|
|
391
|
+
label: KNOWLEDGE_GRAPH_RELATION_LABELS[edge.relationKind],
|
|
392
|
+
items: [peer]
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
const familyGroupsMap = new Map();
|
|
396
|
+
for (const relation of groupsByRelation.values()) {
|
|
397
|
+
const existing = familyGroupsMap.get(relation.family);
|
|
398
|
+
if (existing) {
|
|
399
|
+
existing.relations.push({
|
|
400
|
+
...relation,
|
|
401
|
+
items: [...relation.items].sort((left, right) => left.title.localeCompare(right.title))
|
|
402
|
+
});
|
|
403
|
+
existing.itemCount += relation.items.length;
|
|
404
|
+
existing.relationCount += 1;
|
|
405
|
+
continue;
|
|
406
|
+
}
|
|
407
|
+
familyGroupsMap.set(relation.family, {
|
|
408
|
+
family: relation.family,
|
|
409
|
+
label: KNOWLEDGE_GRAPH_RELATION_FAMILY_LABELS[relation.family],
|
|
410
|
+
relationCount: 1,
|
|
411
|
+
itemCount: relation.items.length,
|
|
412
|
+
relations: [
|
|
413
|
+
{
|
|
414
|
+
...relation,
|
|
415
|
+
items: [...relation.items].sort((left, right) => left.title.localeCompare(right.title))
|
|
416
|
+
}
|
|
417
|
+
]
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
return {
|
|
421
|
+
generatedAt: new Date().toISOString(),
|
|
422
|
+
focusNode,
|
|
423
|
+
firstRingNodes: firstRingNodes.sort((left, right) => left.title.localeCompare(right.title)),
|
|
424
|
+
neighborhoodEdges,
|
|
425
|
+
familyGroups: [...familyGroupsMap.values()]
|
|
426
|
+
.map((group) => ({
|
|
427
|
+
...group,
|
|
428
|
+
relations: group.relations.sort((left, right) => left.label.localeCompare(right.label))
|
|
429
|
+
}))
|
|
430
|
+
.sort((left, right) => left.label.localeCompare(right.label)),
|
|
431
|
+
relationCounts,
|
|
432
|
+
secondRingCounts
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
export function getKnowledgeGraphNodeDegree(nodeId, edges) {
|
|
436
|
+
return edges.reduce((count, edge) => {
|
|
437
|
+
if (edge.source === nodeId || edge.target === nodeId) {
|
|
438
|
+
return count + 1;
|
|
439
|
+
}
|
|
440
|
+
return count;
|
|
441
|
+
}, 0);
|
|
442
|
+
}
|
|
443
|
+
export function isKnowledgeGraphNodeFocused(nodeId, focusNodeId, edges) {
|
|
444
|
+
if (!focusNodeId) {
|
|
445
|
+
return false;
|
|
446
|
+
}
|
|
447
|
+
if (nodeId === focusNodeId) {
|
|
448
|
+
return true;
|
|
449
|
+
}
|
|
450
|
+
return edges.some((edge) => (edge.source === focusNodeId && edge.target === nodeId) ||
|
|
451
|
+
(edge.target === focusNodeId && edge.source === nodeId));
|
|
452
|
+
}
|
|
453
|
+
export function buildKnowledgeGraphFocusNodeId(entityType, entityId) {
|
|
454
|
+
return buildKnowledgeGraphNodeId(entityType, entityId);
|
|
455
|
+
}
|
|
456
|
+
export function getKnowledgeGraphFocusRelatedNodeIds(focusNodeId, edges) {
|
|
457
|
+
if (!focusNodeId) {
|
|
458
|
+
return new Set();
|
|
459
|
+
}
|
|
460
|
+
const related = new Set([focusNodeId]);
|
|
461
|
+
for (const edge of edges) {
|
|
462
|
+
if (edge.source === focusNodeId) {
|
|
463
|
+
related.add(edge.target);
|
|
464
|
+
}
|
|
465
|
+
else if (edge.target === focusNodeId) {
|
|
466
|
+
related.add(edge.source);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
return related;
|
|
470
|
+
}
|
|
@@ -164,6 +164,10 @@ export const quickTaskSchema = z.object({
|
|
|
164
164
|
energy: z.enum(["low", "steady", "high"]),
|
|
165
165
|
dueDate: z.string().trim(),
|
|
166
166
|
points: z.coerce.number().int().min(5).max(500),
|
|
167
|
+
plannedDurationSeconds: z.coerce.number().int().min(60).max(7 * 86_400).nullable().optional(),
|
|
168
|
+
actionCostBand: z
|
|
169
|
+
.enum(["tiny", "light", "standard", "heavy", "brutal"])
|
|
170
|
+
.optional(),
|
|
167
171
|
tagIds: z.array(z.string()),
|
|
168
172
|
notes: z.array(inlineCreateNoteSchema)
|
|
169
173
|
});
|
|
@@ -27,6 +27,8 @@ function normalizeTask(task) {
|
|
|
27
27
|
plannedDurationSeconds: task?.plannedDurationSeconds ?? null,
|
|
28
28
|
schedulingRules: task?.schedulingRules ?? null,
|
|
29
29
|
sortOrder: task?.sortOrder ?? 0,
|
|
30
|
+
resolutionKind: task?.resolutionKind ?? null,
|
|
31
|
+
splitParentTaskId: task?.splitParentTaskId ?? null,
|
|
30
32
|
completedAt: task?.completedAt ?? null,
|
|
31
33
|
createdAt: task?.createdAt ?? new Date(0).toISOString(),
|
|
32
34
|
updatedAt: task?.updatedAt ?? new Date(0).toISOString(),
|
|
@@ -42,6 +44,20 @@ function normalizeTask(task) {
|
|
|
42
44
|
activeRunCount: 0,
|
|
43
45
|
hasCurrentRun: false,
|
|
44
46
|
currentRunId: null
|
|
47
|
+
},
|
|
48
|
+
actionPointSummary: task?.actionPointSummary ?? {
|
|
49
|
+
costBand: "standard",
|
|
50
|
+
totalCostAp: 100,
|
|
51
|
+
expectedDurationSeconds: 86_400,
|
|
52
|
+
sustainRateApPerHour: 100 / 24,
|
|
53
|
+
spentTodayAp: 0,
|
|
54
|
+
spentTotalAp: 0,
|
|
55
|
+
remainingAp: 100
|
|
56
|
+
},
|
|
57
|
+
splitSuggestion: task?.splitSuggestion ?? {
|
|
58
|
+
shouldSplit: false,
|
|
59
|
+
reason: null,
|
|
60
|
+
thresholdSeconds: 2 * 86_400
|
|
45
61
|
}
|
|
46
62
|
};
|
|
47
63
|
}
|
|
@@ -369,6 +385,32 @@ export function normalizeForgeSnapshot(raw) {
|
|
|
369
385
|
tasks: (raw.tasks ?? []).map(normalizeTask),
|
|
370
386
|
habits: (raw.habits ?? raw.dashboard?.habits ?? []).map(normalizeHabit),
|
|
371
387
|
activity: raw.activity ?? [],
|
|
372
|
-
activeTaskRuns: raw.activeTaskRuns ?? []
|
|
388
|
+
activeTaskRuns: raw.activeTaskRuns ?? [],
|
|
389
|
+
lifeForce: raw.lifeForce ?? {
|
|
390
|
+
userId: "",
|
|
391
|
+
dateKey: new Date().toISOString().slice(0, 10),
|
|
392
|
+
baselineDailyAp: 200,
|
|
393
|
+
dailyBudgetAp: 200,
|
|
394
|
+
spentTodayAp: 0,
|
|
395
|
+
remainingAp: 200,
|
|
396
|
+
forecastAp: 0,
|
|
397
|
+
targetBandMinAp: 170,
|
|
398
|
+
targetBandMaxAp: 200,
|
|
399
|
+
instantCapacityApPerHour: 0,
|
|
400
|
+
instantFreeApPerHour: 0,
|
|
401
|
+
overloadApPerHour: 0,
|
|
402
|
+
currentDrainApPerHour: 0,
|
|
403
|
+
fatigueBufferApPerHour: 0,
|
|
404
|
+
sleepRecoveryMultiplier: 1,
|
|
405
|
+
readinessMultiplier: 1,
|
|
406
|
+
fatigueDebtCarry: 0,
|
|
407
|
+
stats: [],
|
|
408
|
+
currentCurve: [],
|
|
409
|
+
activeDrains: [],
|
|
410
|
+
warnings: [],
|
|
411
|
+
recommendations: [],
|
|
412
|
+
topTaskIdsNeedingSplit: [],
|
|
413
|
+
updatedAt: new Date(0).toISOString()
|
|
414
|
+
}
|
|
373
415
|
};
|
|
374
416
|
}
|