atlas-mcp 0.1.0

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 (54) hide show
  1. package/.env.example +32 -0
  2. package/README.md +282 -0
  3. package/package.json +72 -0
  4. package/public/app/assets/app-CxbS1w9p.js +3981 -0
  5. package/public/app/assets/index-BA6nxCuI.css +1 -0
  6. package/public/app/assets/index-BXmIRrQH.js +177 -0
  7. package/public/app/index.html +27 -0
  8. package/public/assets/brain-atlas.LICENSE.txt +16 -0
  9. package/public/assets/brain-atlas.glb +0 -0
  10. package/public/assets/brain.obj +27282 -0
  11. package/public/fonts/DepartureMono-Regular.woff +0 -0
  12. package/public/fonts/DepartureMono-Regular.woff2 +0 -0
  13. package/scripts/sync-memory-vectors.js +46 -0
  14. package/src/audit.js +9 -0
  15. package/src/cli/args.js +87 -0
  16. package/src/cli/commands/add.js +103 -0
  17. package/src/cli/commands/config.js +228 -0
  18. package/src/cli/commands/delete.js +75 -0
  19. package/src/cli/commands/entities.js +39 -0
  20. package/src/cli/commands/entity.js +47 -0
  21. package/src/cli/commands/get.js +46 -0
  22. package/src/cli/commands/list.js +53 -0
  23. package/src/cli/commands/related.js +56 -0
  24. package/src/cli/commands/search.js +68 -0
  25. package/src/cli/commands/update.js +58 -0
  26. package/src/cli/deps.js +114 -0
  27. package/src/cli/env-file.js +44 -0
  28. package/src/cli/format.js +246 -0
  29. package/src/cli.js +187 -0
  30. package/src/cognitive-worker.js +381 -0
  31. package/src/db.js +2674 -0
  32. package/src/extraction-context.js +31 -0
  33. package/src/ingestion-service.js +387 -0
  34. package/src/ingestion-worker.js +225 -0
  35. package/src/llm-config.js +31 -0
  36. package/src/llm.js +789 -0
  37. package/src/logger.js +51 -0
  38. package/src/mcp-server.js +577 -0
  39. package/src/memory-comparison.js +421 -0
  40. package/src/related-memories.js +232 -0
  41. package/src/run-cognitive-worker.js +12 -0
  42. package/src/run-ingestion-worker.js +13 -0
  43. package/src/run-vector-worker.js +12 -0
  44. package/src/schemas.js +413 -0
  45. package/src/semantic-validation.js +430 -0
  46. package/src/server.js +827 -0
  47. package/src/shared/brain-regions.js +61 -0
  48. package/src/shared/entity-lens.js +249 -0
  49. package/src/shared/memory-placement.js +171 -0
  50. package/src/shared/memory-search.js +55 -0
  51. package/src/shared/region-anchors.js +112 -0
  52. package/src/shared/region-mapper.js +247 -0
  53. package/src/vector-store.js +546 -0
  54. package/src/vector-worker.js +71 -0
@@ -0,0 +1,61 @@
1
+ export const CORE_BRAIN_REGIONS = Object.freeze([
2
+ "hippocampus",
3
+ "prefrontal",
4
+ "temporalCortex",
5
+ "parietalCortex",
6
+ "amygdala",
7
+ "basalGanglia",
8
+ "cerebellum",
9
+ "motorCortex",
10
+ "insula",
11
+ ]);
12
+
13
+ const SURFACE_REGION_PROFILES = Object.freeze({
14
+ prefrontal: {
15
+ center: [0, 0.35, 1.8],
16
+ radius: [1.8, 1.45, 1.15],
17
+ },
18
+ temporalCortex: {
19
+ center: [1.45, -0.5, 0.35],
20
+ radius: [0.85, 1.1, 1.45],
21
+ bilateral: true,
22
+ },
23
+ parietalCortex: {
24
+ center: [0, 1.2, -0.9],
25
+ radius: [1.9, 1.05, 1.35],
26
+ },
27
+ cerebellum: {
28
+ center: [0, -1.55, -1.45],
29
+ radius: [1.45, 0.7, 1.1],
30
+ },
31
+ motorCortex: {
32
+ center: [0, 1.25, 0.25],
33
+ radius: [1.9, 0.7, 0.75],
34
+ },
35
+ insula: {
36
+ center: [1.25, -0.05, 0.45],
37
+ radius: [0.7, 0.8, 0.8],
38
+ bilateral: true,
39
+ },
40
+ });
41
+
42
+ export function getSurfaceBrainRegion(point) {
43
+ let closestRegion = null;
44
+ let closestDistance = Infinity;
45
+
46
+ for (const [region, profile] of Object.entries(SURFACE_REGION_PROFILES)) {
47
+ const x = profile.bilateral ? Math.abs(point[0]) : point[0];
48
+ const distance = profile.center.reduce((total, center, index) => {
49
+ const coordinate = index === 0 ? x : point[index];
50
+ const normalized = (coordinate - center) / profile.radius[index];
51
+ return total + normalized * normalized;
52
+ }, 0);
53
+
54
+ if (distance < closestDistance) {
55
+ closestRegion = region;
56
+ closestDistance = distance;
57
+ }
58
+ }
59
+
60
+ return closestRegion;
61
+ }
@@ -0,0 +1,249 @@
1
+ export const ENTITY_KIND_COLORS = Object.freeze({
2
+ person: "#9bbcff",
3
+ place: "#7ee8d3",
4
+ object: "#ffd38a",
5
+ concept: "#e99fd1",
6
+ organization: "#ffad8a",
7
+ });
8
+
9
+ const HUB_OUTWARD_OFFSET = 0.62;
10
+ const HUB_TANGENT_OFFSET = 0.12;
11
+ const PREVIEW_OUTWARD_OFFSET = 0.36;
12
+ const PREVIEW_TANGENT_OFFSET = 0.58;
13
+
14
+ export function calculateEntityHubPosition(originPosition, entityId) {
15
+ const origin = toVector(originPosition);
16
+ const outward = normalize(origin, [0, 0, 1]);
17
+ const [tangentA, tangentB] = getTangents(outward);
18
+ const angle = hashUnit(`entity:${entityId}:hub`) * Math.PI * 2;
19
+
20
+ return clampToBrainBoundary(
21
+ addVectors(
22
+ origin,
23
+ scaleVector(outward, HUB_OUTWARD_OFFSET),
24
+ scaleVector(tangentA, Math.cos(angle) * HUB_TANGENT_OFFSET),
25
+ scaleVector(tangentB, Math.sin(angle) * HUB_TANGENT_OFFSET),
26
+ ),
27
+ );
28
+ }
29
+
30
+ export function calculateRelationshipPreviewPosition(
31
+ hubPosition,
32
+ counterpartEntityId,
33
+ direction,
34
+ ) {
35
+ const hub = toVector(hubPosition);
36
+ const outward = normalize(hub, [0, 0, 1]);
37
+ const [tangentA, tangentB] = getTangents(outward);
38
+ const directionOffset = direction === "incoming" ? Math.PI : 0;
39
+ const angle =
40
+ hashUnit(`entity:${counterpartEntityId}:preview`) * Math.PI * 2 +
41
+ directionOffset;
42
+
43
+ return clampToBrainBoundary(
44
+ addVectors(
45
+ hub,
46
+ scaleVector(outward, PREVIEW_OUTWARD_OFFSET),
47
+ scaleVector(tangentA, Math.cos(angle) * PREVIEW_TANGENT_OFFSET),
48
+ scaleVector(tangentB, Math.sin(angle) * PREVIEW_TANGENT_OFFSET),
49
+ ),
50
+ );
51
+ }
52
+
53
+ export function getRelationshipDirection(relationship, entityId) {
54
+ const activeId = Number(entityId);
55
+ if (Number(relationship?.source?.id) === activeId) return "outgoing";
56
+ if (Number(relationship?.target?.id) === activeId) return "incoming";
57
+ return null;
58
+ }
59
+
60
+ export function getRelationshipCounterpart(relationship, entityId) {
61
+ const direction = getRelationshipDirection(relationship, entityId);
62
+ if (direction === "outgoing") return relationship.target;
63
+ if (direction === "incoming") return relationship.source;
64
+ return null;
65
+ }
66
+
67
+ export function filterEntityGraphMemories(graph, visibleMemoryIds) {
68
+ const visibleIds =
69
+ visibleMemoryIds instanceof Set
70
+ ? visibleMemoryIds
71
+ : new Set(visibleMemoryIds || []);
72
+ const unique = new Map();
73
+
74
+ for (const memory of graph?.memories || []) {
75
+ if (memory?.id && !unique.has(memory.id)) unique.set(memory.id, memory);
76
+ }
77
+
78
+ const memories = [...unique.values()];
79
+ const visible = memories.filter((memory) => visibleIds.has(memory.id));
80
+ return {
81
+ visible,
82
+ hidden: memories.length - visible.length,
83
+ total: memories.length,
84
+ };
85
+ }
86
+
87
+ export function buildEntitySpokes(graph, visibleMemoryIds) {
88
+ const { visible, hidden, total } = filterEntityGraphMemories(
89
+ graph,
90
+ visibleMemoryIds,
91
+ );
92
+ return {
93
+ spokes: visible.map((memory) => ({
94
+ id: `${graph.entity.id}:${memory.id}`,
95
+ entityId: graph.entity.id,
96
+ memoryId: memory.id,
97
+ })),
98
+ hidden,
99
+ total,
100
+ };
101
+ }
102
+
103
+ export function normalizeMemoryLinkResponse(
104
+ response,
105
+ selectedMemoryId,
106
+ limit = 5,
107
+ ) {
108
+ const normalizedLimit = Number.isInteger(limit) && limit > 0 ? limit : 5;
109
+ const selectedId = String(selectedMemoryId);
110
+ const seen = new Set();
111
+ const links = [];
112
+
113
+ for (const link of response?.links || []) {
114
+ const memoryId = link?.memory?.id;
115
+ if (memoryId == null) continue;
116
+
117
+ const id = String(memoryId);
118
+ if (id === selectedId || seen.has(id)) continue;
119
+ seen.add(id);
120
+
121
+ const score = Number(link.score);
122
+ const semanticSimilarity =
123
+ link.semanticSimilarity == null
124
+ ? null
125
+ : Number(link.semanticSimilarity);
126
+ links.push({
127
+ ...link,
128
+ score: Number.isFinite(score) ? clampUnit(score) : 0,
129
+ reasons: Array.isArray(link.reasons) ? link.reasons : [],
130
+ sharedEntities: Array.isArray(link.sharedEntities)
131
+ ? link.sharedEntities
132
+ : [],
133
+ sharedRelationships: Array.isArray(link.sharedRelationships)
134
+ ? link.sharedRelationships
135
+ : [],
136
+ semanticSimilarity: Number.isFinite(semanticSimilarity)
137
+ ? clampUnit(semanticSimilarity)
138
+ : null,
139
+ });
140
+
141
+ if (links.length === normalizedLimit) break;
142
+ }
143
+
144
+ return {
145
+ memoryId: response?.memoryId ?? selectedMemoryId,
146
+ links,
147
+ semanticAvailable: response?.semanticAvailable !== false,
148
+ };
149
+ }
150
+
151
+ export function filterMemoryLinksForVisibleMemories(
152
+ links,
153
+ visibleMemoryIds,
154
+ limit = 5,
155
+ ) {
156
+ const visibleIds =
157
+ visibleMemoryIds instanceof Set
158
+ ? visibleMemoryIds
159
+ : new Set(visibleMemoryIds || []);
160
+ const normalizedLimit = Number.isInteger(limit) && limit > 0 ? limit : 5;
161
+ const visible = [];
162
+
163
+ for (const link of links || []) {
164
+ if (!visibleIds.has(link?.memory?.id)) continue;
165
+ visible.push(link);
166
+ if (visible.length === normalizedLimit) break;
167
+ }
168
+
169
+ return visible;
170
+ }
171
+
172
+ export function pushNavigation(history, entry) {
173
+ const current = history.at(-1);
174
+ if (
175
+ current?.type === entry?.type &&
176
+ String(current?.id) === String(entry?.id)
177
+ ) {
178
+ return [...history];
179
+ }
180
+ return [...history, { ...entry }];
181
+ }
182
+
183
+ export function restoreNavigation(history, index) {
184
+ if (!Number.isInteger(index) || index < 0 || index >= history.length) {
185
+ return { history: [...history], current: null };
186
+ }
187
+ const restored = history.slice(0, index + 1).map((entry) => ({ ...entry }));
188
+ return { history: restored, current: restored.at(-1) };
189
+ }
190
+
191
+ function toVector(value) {
192
+ if (!Array.isArray(value) || value.length !== 3) return [0, 0, 0];
193
+ return value.map((coordinate) =>
194
+ Number.isFinite(Number(coordinate)) ? Number(coordinate) : 0,
195
+ );
196
+ }
197
+
198
+ function normalize(vector, fallback) {
199
+ const length = Math.hypot(...vector);
200
+ return length > 1e-9
201
+ ? vector.map((coordinate) => coordinate / length)
202
+ : [...fallback];
203
+ }
204
+
205
+ function getTangents(outward) {
206
+ const reference = Math.abs(outward[1]) < 0.9 ? [0, 1, 0] : [1, 0, 0];
207
+ const tangentA = normalize(cross(outward, reference), [1, 0, 0]);
208
+ return [tangentA, normalize(cross(outward, tangentA), [0, 1, 0])];
209
+ }
210
+
211
+ function addVectors(...vectors) {
212
+ return [0, 1, 2].map((index) =>
213
+ vectors.reduce((sum, vector) => sum + vector[index], 0),
214
+ );
215
+ }
216
+
217
+ function scaleVector(vector, scale) {
218
+ return vector.map((coordinate) => coordinate * scale);
219
+ }
220
+
221
+ function cross(a, b) {
222
+ return [
223
+ a[1] * b[2] - a[2] * b[1],
224
+ a[2] * b[0] - a[0] * b[2],
225
+ a[0] * b[1] - a[1] * b[0],
226
+ ];
227
+ }
228
+
229
+ function hashUnit(value) {
230
+ let hash = 2166136261;
231
+ for (let index = 0; index < value.length; index += 1) {
232
+ hash ^= value.charCodeAt(index);
233
+ hash = Math.imul(hash, 16777619);
234
+ }
235
+ return (hash >>> 0) / 4294967296;
236
+ }
237
+
238
+ function clampUnit(value) {
239
+ return Math.min(Math.max(value, 0), 1);
240
+ }
241
+
242
+ const BRAIN_RADIUS = 1.95;
243
+
244
+ function clampToBrainBoundary(position) {
245
+ const length = Math.hypot(...position);
246
+ if (length <= BRAIN_RADIUS) return position;
247
+ const scale = BRAIN_RADIUS / length;
248
+ return position.map((v) => v * scale);
249
+ }
@@ -0,0 +1,171 @@
1
+ import { getRegionAnchor } from "./region-anchors.js";
2
+
3
+ const MIN_REGION_INSET = 0.34;
4
+ const REGION_INSET_RANGE = 0.14;
5
+ const MIN_TANGENT_OFFSET = 0.04;
6
+ const TANGENT_OFFSET_RANGE = 0.14;
7
+
8
+ export function getDominantRegion(regions) {
9
+ let dominant = null;
10
+
11
+ for (const activation of regions || []) {
12
+ if (
13
+ !getRegionAnchor(activation.region) ||
14
+ !Number.isFinite(activation.weight)
15
+ ) {
16
+ continue;
17
+ }
18
+
19
+ if (
20
+ !dominant ||
21
+ activation.weight > dominant.weight ||
22
+ (activation.weight === dominant.weight &&
23
+ activation.region.localeCompare(dominant.region) < 0)
24
+ ) {
25
+ dominant = activation;
26
+ }
27
+ }
28
+
29
+ return dominant?.region || null;
30
+ }
31
+
32
+ export function calculateMemoryPosition(memoryId, regions) {
33
+ const dominantRegion = getDominantRegion(regions);
34
+ const dominantActivation = (regions || []).find(
35
+ ({ region }) => region === dominantRegion,
36
+ );
37
+ return calculateRegionPosition(
38
+ memoryId,
39
+ dominantRegion,
40
+ getActivationAnchorPosition(dominantActivation),
41
+ );
42
+ }
43
+
44
+ export function calculateRegionPosition(memoryId, region, positionOverride) {
45
+ const anchor = getRegionAnchor(region);
46
+ if (!anchor) return null;
47
+ const anchorPosition = positionOverride || anchor.position;
48
+
49
+ const outward = normalize(anchorPosition);
50
+ const reference =
51
+ Math.abs(outward[1]) < 0.9 ? [0, 1, 0] : [1, 0, 0];
52
+ const tangentA = normalize(cross(outward, reference));
53
+ const tangentB = cross(outward, tangentA);
54
+
55
+ const angle = hashUnit(`${memoryId}:${region}:angle`) * Math.PI * 2;
56
+ const tangentDistance =
57
+ MIN_TANGENT_OFFSET +
58
+ hashUnit(`${memoryId}:${region}:distance`) * TANGENT_OFFSET_RANGE;
59
+ const inset =
60
+ MIN_REGION_INSET +
61
+ hashUnit(`${memoryId}:${region}:inset`) * REGION_INSET_RANGE;
62
+ const tangentX = Math.cos(angle) * tangentDistance;
63
+ const tangentY = Math.sin(angle) * tangentDistance;
64
+
65
+ const position = anchorPosition.map(
66
+ (coordinate, index) =>
67
+ coordinate +
68
+ outward[index] * -inset +
69
+ tangentA[index] * tangentX +
70
+ tangentB[index] * tangentY,
71
+ );
72
+
73
+ return clampToBrainBoundary(position);
74
+ }
75
+
76
+ function getActivationAnchorPosition(activation) {
77
+ if (activation?.region !== "hippocampus") return null;
78
+
79
+ const anchor = getRegionAnchor("hippocampus");
80
+ const { left, right } = activation.hemispheres || {};
81
+ const total = left + right;
82
+ if (
83
+ !anchor?.hemispherePositions
84
+ || !Number.isFinite(left)
85
+ || !Number.isFinite(right)
86
+ || total <= 0
87
+ ) {
88
+ return anchor?.position || null;
89
+ }
90
+
91
+ const leftShare = left / total;
92
+ const rightShare = right / total;
93
+ return anchor.hemispherePositions.left.map(
94
+ (coordinate, index) =>
95
+ coordinate * leftShare
96
+ + anchor.hemispherePositions.right[index] * rightShare,
97
+ );
98
+ }
99
+
100
+ export function createMemoryNodeState(memories) {
101
+ const state = new Map();
102
+
103
+ for (const memory of memories) {
104
+ const activations = (memory.regions || [])
105
+ .filter(
106
+ ({ region, weight }) =>
107
+ getRegionAnchor(region) && Number.isFinite(weight) && weight > 0,
108
+ )
109
+ .sort(
110
+ (a, b) => b.weight - a.weight || a.region.localeCompare(b.region),
111
+ );
112
+ const dominantRegion = activations[0]?.region;
113
+ if (!dominantRegion) continue;
114
+
115
+ const normalizedActivations = activations.map(
116
+ ({ region, weight, hemispheres }, index) => ({
117
+ region,
118
+ weight,
119
+ ...(hemispheres ? { hemispheres } : {}),
120
+ isDominant: index === 0,
121
+ }),
122
+ );
123
+
124
+ state.set(memory.id, {
125
+ core: {
126
+ region: dominantRegion,
127
+ weight: normalizedActivations[0].weight,
128
+ ...(normalizedActivations[0].hemispheres
129
+ ? { hemispheres: normalizedActivations[0].hemispheres }
130
+ : {}),
131
+ position: calculateMemoryPosition(memory.id, activations),
132
+ },
133
+ activations: normalizedActivations,
134
+ });
135
+ }
136
+
137
+ return state;
138
+ }
139
+
140
+ const BRAIN_RADIUS = 1.95;
141
+
142
+ function clampToBrainBoundary(position) {
143
+ const length = Math.hypot(...position);
144
+ if (length <= BRAIN_RADIUS) return position;
145
+ const scale = BRAIN_RADIUS / length;
146
+ return position.map((v) => v * scale);
147
+ }
148
+
149
+ function hashUnit(value) {
150
+ let hash = 2166136261;
151
+
152
+ for (let index = 0; index < value.length; index += 1) {
153
+ hash ^= value.charCodeAt(index);
154
+ hash = Math.imul(hash, 16777619);
155
+ }
156
+
157
+ return (hash >>> 0) / 4294967296;
158
+ }
159
+
160
+ function normalize(vector) {
161
+ const length = Math.hypot(...vector);
162
+ return vector.map((value) => value / length);
163
+ }
164
+
165
+ function cross(a, b) {
166
+ return [
167
+ a[1] * b[2] - a[2] * b[1],
168
+ a[2] * b[0] - a[0] * b[2],
169
+ a[0] * b[1] - a[1] * b[0],
170
+ ];
171
+ }
@@ -0,0 +1,55 @@
1
+ function normalizeSearchValue(value) {
2
+ return String(value || "").trim().toLocaleLowerCase();
3
+ }
4
+
5
+ function getEntitySearchValues(entity) {
6
+ return [
7
+ entity?.canonical_name,
8
+ entity?.canonicalName,
9
+ entity?.mention,
10
+ ];
11
+ }
12
+
13
+ export function matchesMemorySearch(memory, query) {
14
+ const terms = normalizeSearchValue(query).split(/\s+/).filter(Boolean);
15
+ if (!terms.length) return true;
16
+
17
+ const extractionEntities = memory.extraction?.entities || [];
18
+ const searchableText = normalizeSearchValue(
19
+ [
20
+ memory.text,
21
+ memory.summary,
22
+ ...(memory.entities || []).flatMap(getEntitySearchValues),
23
+ ...extractionEntities.flatMap(getEntitySearchValues),
24
+ ].join(" "),
25
+ );
26
+
27
+ return terms.every((term) => searchableText.includes(term));
28
+ }
29
+
30
+ export function filterMemoriesForSearch(
31
+ memories,
32
+ { query = "", source = "all", semanticIds = null } = {},
33
+ ) {
34
+ const sourceMatches = (memory) =>
35
+ source === "all" || memory.source === source;
36
+ const normalizedQuery = normalizeSearchValue(query);
37
+
38
+ if (!normalizedQuery) return memories.filter(sourceMatches);
39
+
40
+ if (Array.isArray(semanticIds)) {
41
+ const memoriesById = new Map(
42
+ memories.map((memory) => [String(memory.id), memory]),
43
+ );
44
+ return semanticIds
45
+ .map((id) => memoriesById.get(String(id)) || null)
46
+ .filter(
47
+ (memory) => memory !== null && memory !== undefined && sourceMatches(memory),
48
+ );
49
+ }
50
+
51
+ return memories.filter(
52
+ (memory) =>
53
+ sourceMatches(memory) && matchesMemorySearch(memory, normalizedQuery),
54
+ );
55
+ }
@@ -0,0 +1,112 @@
1
+ export const REGION_COLORS = Object.freeze({
2
+ hippocampus: "#ffd38a",
3
+ prefrontal: "#FF7A90",
4
+ amygdala: "#ff8fa8",
5
+ temporalCortex: "#A78BFA",
6
+ parietalCortex: "#7ee8d3",
7
+ basalGanglia: "#8B7CFF",
8
+ cerebellum: "#7EE0B8",
9
+ motorCortex: "#F6B26B",
10
+ insula: "#e99fd1",
11
+ associationCortex: "#5B7CFF",
12
+ entorhinal: "#80d8d0",
13
+ });
14
+
15
+ export const REGION_ANCHORS = Object.freeze({
16
+ hippocampus: {
17
+ label: "Hippocampus",
18
+ role: "Binds events to their spatial and temporal context.",
19
+ position: [0, -0.85, 0.8],
20
+ // The calibrated brain model uses negative X for anatomical left.
21
+ hemispherePositions: {
22
+ left: [-0.93, -0.85, 0.8],
23
+ right: [0.93, -0.85, 0.8],
24
+ },
25
+ color: REGION_COLORS.hippocampus,
26
+ markerScale: 0.85,
27
+ },
28
+ prefrontal: {
29
+ label: "Prefrontal cortex",
30
+ role: "Supports active recall, planning, and working control.",
31
+ position: [0, 0.35, 2.28],
32
+ activationRadius: [1.15, 1.05, 0.78],
33
+ color: REGION_COLORS.prefrontal,
34
+ markerScale: 1.1,
35
+ },
36
+ associationCortex: {
37
+ label: "Association cortex",
38
+ role: "Integrates concepts across knowledge and sensory systems.",
39
+ position: [-1.25, 0.75, 0.85],
40
+ activationRadius: [0.95, 1.05, 1.05],
41
+ color: REGION_COLORS.associationCortex,
42
+ markerScale: 1.15,
43
+ },
44
+ temporalCortex: {
45
+ label: "Temporal cortex",
46
+ role: "Supports semantic knowledge and recognizable concepts.",
47
+ position: [1.72, -0.45, 0.35],
48
+ activationRadius: [0.62, 0.9, 1.12],
49
+ color: REGION_COLORS.temporalCortex,
50
+ markerScale: 1.1,
51
+ },
52
+ basalGanglia: {
53
+ label: "Basal ganglia",
54
+ role: "Supports learned routines, habits, and procedural patterns.",
55
+ position: [0.62, -0.12, 0.25],
56
+ activationRadius: [0.95, 0.82, 0.95],
57
+ color: REGION_COLORS.basalGanglia,
58
+ markerScale: 0.8,
59
+ },
60
+ cerebellum: {
61
+ label: "Cerebellum",
62
+ role: "Coordinates timing and precision in practiced movement.",
63
+ position: [0.45, -1.3, -1.15],
64
+ activationRadius: [1.28, 0.72, 1],
65
+ color: REGION_COLORS.cerebellum,
66
+ markerScale: 1.1,
67
+ },
68
+ motorCortex: {
69
+ label: "Motor cortex",
70
+ role: "Represents and plans physical actions.",
71
+ position: [1.15, 1.35, 0.05],
72
+ activationRadius: [0.72, 0.82, 1.05],
73
+ color: REGION_COLORS.motorCortex,
74
+ markerScale: 1,
75
+ },
76
+ amygdala: {
77
+ label: "Amygdala",
78
+ role: "Tags experiences with emotional salience.",
79
+ position: [0.82, -0.78, 0.72],
80
+ activationRadius: [0.82, 0.72, 0.82],
81
+ color: REGION_COLORS.amygdala,
82
+ markerScale: 0.72,
83
+ },
84
+ insula: {
85
+ label: "Insula",
86
+ role: "Represents internal feelings and bodily state.",
87
+ position: [1.48, 0.02, 0.48],
88
+ activationRadius: [0.7, 0.82, 0.82],
89
+ color: REGION_COLORS.insula,
90
+ markerScale: 0.9,
91
+ },
92
+ entorhinal: {
93
+ label: "Entorhinal cortex",
94
+ role: "Supports spatial context and navigation through memory.",
95
+ position: [1.08, -0.92, 0.18],
96
+ activationRadius: [0.82, 0.68, 0.88],
97
+ color: REGION_COLORS.entorhinal,
98
+ markerScale: 0.76,
99
+ },
100
+ parietalCortex: {
101
+ label: "Parietal cortex",
102
+ role: "Supports spatial attention and active information.",
103
+ position: [0.82, 1.3, -1.08],
104
+ activationRadius: [1.05, 0.82, 0.95],
105
+ color: REGION_COLORS.parietalCortex,
106
+ markerScale: 1.1,
107
+ },
108
+ });
109
+
110
+ export function getRegionAnchor(region) {
111
+ return REGION_ANCHORS[region];
112
+ }