holosphere 1.1.20 → 2.0.0-alpha0

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 (146) hide show
  1. package/.env.example +36 -0
  2. package/.eslintrc.json +16 -0
  3. package/.prettierrc.json +7 -0
  4. package/README.md +483 -367
  5. package/bin/holosphere-activitypub.js +158 -0
  6. package/cleanup-test-data.js +204 -0
  7. package/examples/demo.html +1333 -0
  8. package/examples/example-bot.js +197 -0
  9. package/package.json +47 -87
  10. package/scripts/check-bundle-size.js +54 -0
  11. package/scripts/check-quest-ids.js +77 -0
  12. package/scripts/import-holons.js +578 -0
  13. package/scripts/publish-to-relay.js +101 -0
  14. package/scripts/read-example.js +186 -0
  15. package/scripts/relay-diagnostic.js +59 -0
  16. package/scripts/relay-example.js +179 -0
  17. package/scripts/resync-to-relay.js +245 -0
  18. package/scripts/revert-import.js +196 -0
  19. package/scripts/test-hybrid-mode.js +108 -0
  20. package/scripts/test-local-storage.js +63 -0
  21. package/scripts/test-nostr-direct.js +55 -0
  22. package/scripts/test-read-data.js +45 -0
  23. package/scripts/test-write-read.js +63 -0
  24. package/scripts/verify-import.js +95 -0
  25. package/scripts/verify-relay-data.js +139 -0
  26. package/src/ai/aggregation.js +319 -0
  27. package/src/ai/breakdown.js +511 -0
  28. package/src/ai/classifier.js +217 -0
  29. package/src/ai/council.js +228 -0
  30. package/src/ai/embeddings.js +279 -0
  31. package/src/ai/federation-ai.js +324 -0
  32. package/src/ai/h3-ai.js +955 -0
  33. package/src/ai/index.js +112 -0
  34. package/src/ai/json-ops.js +225 -0
  35. package/src/ai/llm-service.js +205 -0
  36. package/src/ai/nl-query.js +223 -0
  37. package/src/ai/relationships.js +353 -0
  38. package/src/ai/schema-extractor.js +218 -0
  39. package/src/ai/spatial.js +293 -0
  40. package/src/ai/tts.js +194 -0
  41. package/src/content/social-protocols.js +168 -0
  42. package/src/core/holosphere.js +273 -0
  43. package/src/crypto/secp256k1.js +259 -0
  44. package/src/federation/discovery.js +334 -0
  45. package/src/federation/hologram.js +1042 -0
  46. package/src/federation/registry.js +386 -0
  47. package/src/hierarchical/upcast.js +110 -0
  48. package/src/index.js +2669 -0
  49. package/src/schema/validator.js +91 -0
  50. package/src/spatial/h3-operations.js +110 -0
  51. package/src/storage/backend-factory.js +125 -0
  52. package/src/storage/backend-interface.js +142 -0
  53. package/src/storage/backends/activitypub/server.js +653 -0
  54. package/src/storage/backends/activitypub-backend.js +272 -0
  55. package/src/storage/backends/gundb-backend.js +233 -0
  56. package/src/storage/backends/nostr-backend.js +136 -0
  57. package/src/storage/filesystem-storage-browser.js +41 -0
  58. package/src/storage/filesystem-storage.js +138 -0
  59. package/src/storage/global-tables.js +81 -0
  60. package/src/storage/gun-async.js +281 -0
  61. package/src/storage/gun-wrapper.js +221 -0
  62. package/src/storage/indexeddb-storage.js +122 -0
  63. package/src/storage/key-storage-simple.js +76 -0
  64. package/src/storage/key-storage.js +136 -0
  65. package/src/storage/memory-storage.js +59 -0
  66. package/src/storage/migration.js +338 -0
  67. package/src/storage/nostr-async.js +811 -0
  68. package/src/storage/nostr-client.js +939 -0
  69. package/src/storage/nostr-wrapper.js +211 -0
  70. package/src/storage/outbox-queue.js +208 -0
  71. package/src/storage/persistent-storage.js +109 -0
  72. package/src/storage/sync-service.js +164 -0
  73. package/src/subscriptions/manager.js +142 -0
  74. package/test-ai-real-api.js +202 -0
  75. package/tests/unit/ai/aggregation.test.js +295 -0
  76. package/tests/unit/ai/breakdown.test.js +446 -0
  77. package/tests/unit/ai/classifier.test.js +294 -0
  78. package/tests/unit/ai/council.test.js +262 -0
  79. package/tests/unit/ai/embeddings.test.js +384 -0
  80. package/tests/unit/ai/federation-ai.test.js +344 -0
  81. package/tests/unit/ai/h3-ai.test.js +458 -0
  82. package/tests/unit/ai/index.test.js +304 -0
  83. package/tests/unit/ai/json-ops.test.js +307 -0
  84. package/tests/unit/ai/llm-service.test.js +390 -0
  85. package/tests/unit/ai/nl-query.test.js +383 -0
  86. package/tests/unit/ai/relationships.test.js +311 -0
  87. package/tests/unit/ai/schema-extractor.test.js +384 -0
  88. package/tests/unit/ai/spatial.test.js +279 -0
  89. package/tests/unit/ai/tts.test.js +279 -0
  90. package/tests/unit/content.test.js +332 -0
  91. package/tests/unit/contract/core.test.js +88 -0
  92. package/tests/unit/contract/crypto.test.js +198 -0
  93. package/tests/unit/contract/data.test.js +223 -0
  94. package/tests/unit/contract/federation.test.js +181 -0
  95. package/tests/unit/contract/hierarchical.test.js +113 -0
  96. package/tests/unit/contract/schema.test.js +114 -0
  97. package/tests/unit/contract/social.test.js +217 -0
  98. package/tests/unit/contract/spatial.test.js +110 -0
  99. package/tests/unit/contract/subscriptions.test.js +128 -0
  100. package/tests/unit/contract/utils.test.js +159 -0
  101. package/tests/unit/core.test.js +152 -0
  102. package/tests/unit/crypto.test.js +328 -0
  103. package/tests/unit/federation.test.js +234 -0
  104. package/tests/unit/gun-async.test.js +252 -0
  105. package/tests/unit/hierarchical.test.js +399 -0
  106. package/tests/unit/integration/scenario-01-geographic-storage.test.js +74 -0
  107. package/tests/unit/integration/scenario-02-federation.test.js +76 -0
  108. package/tests/unit/integration/scenario-03-subscriptions.test.js +102 -0
  109. package/tests/unit/integration/scenario-04-validation.test.js +129 -0
  110. package/tests/unit/integration/scenario-05-hierarchy.test.js +125 -0
  111. package/tests/unit/integration/scenario-06-social.test.js +135 -0
  112. package/tests/unit/integration/scenario-07-persistence.test.js +130 -0
  113. package/tests/unit/integration/scenario-08-authorization.test.js +161 -0
  114. package/tests/unit/integration/scenario-09-cross-dimensional.test.js +139 -0
  115. package/tests/unit/integration/scenario-10-cross-holosphere-capabilities.test.js +357 -0
  116. package/tests/unit/integration/scenario-11-cross-holosphere-federation.test.js +410 -0
  117. package/tests/unit/integration/scenario-12-capability-federated-read.test.js +719 -0
  118. package/tests/unit/performance/benchmark.test.js +85 -0
  119. package/tests/unit/schema.test.js +213 -0
  120. package/tests/unit/spatial.test.js +158 -0
  121. package/tests/unit/storage.test.js +195 -0
  122. package/tests/unit/subscriptions.test.js +328 -0
  123. package/tests/unit/test-data-permanence-debug.js +197 -0
  124. package/tests/unit/test-data-permanence.js +340 -0
  125. package/tests/unit/test-key-persistence-fixed.js +148 -0
  126. package/tests/unit/test-key-persistence.js +172 -0
  127. package/tests/unit/test-relay-permanence.js +376 -0
  128. package/tests/unit/test-second-node.js +95 -0
  129. package/tests/unit/test-simple-write.js +89 -0
  130. package/vite.config.js +49 -0
  131. package/vitest.config.js +20 -0
  132. package/FEDERATION.md +0 -213
  133. package/compute.js +0 -298
  134. package/content.js +0 -980
  135. package/federation.js +0 -1234
  136. package/global.js +0 -736
  137. package/hexlib.js +0 -335
  138. package/hologram.js +0 -183
  139. package/holosphere-bundle.esm.js +0 -33256
  140. package/holosphere-bundle.js +0 -33287
  141. package/holosphere-bundle.min.js +0 -39
  142. package/holosphere.d.ts +0 -601
  143. package/holosphere.js +0 -719
  144. package/node.js +0 -246
  145. package/schema.js +0 -139
  146. package/utils.js +0 -302
@@ -0,0 +1,955 @@
1
+ /**
2
+ * H3 AI - Geospatial intelligence for H3 hexagonal hierarchies
3
+ */
4
+
5
+ import * as h3 from 'h3-js';
6
+
7
+ /**
8
+ * H3 AI class for geospatial intelligence
9
+ */
10
+ export class H3AI {
11
+ /**
12
+ * @param {LLMService} llmService - LLM service instance
13
+ * @param {Object} holosphere - HoloSphere instance
14
+ */
15
+ constructor(llmService, holosphere = null) {
16
+ this.llm = llmService;
17
+ this.holosphere = holosphere;
18
+ }
19
+
20
+ /**
21
+ * Set HoloSphere instance
22
+ * @param {Object} holosphere - HoloSphere instance
23
+ */
24
+ setHoloSphere(holosphere) {
25
+ this.holosphere = holosphere;
26
+ }
27
+
28
+ /**
29
+ * Suggest optimal H3 resolution for a task/project based on its scope
30
+ * @param {Object} item - Item to analyze (task, project, quest)
31
+ * @param {Object} options - Options
32
+ * @returns {Promise<Object>} Resolution recommendation
33
+ */
34
+ async suggestResolution(item, options = {}) {
35
+ const { currentResolution = null, context = null } = options;
36
+
37
+ const systemPrompt = `You are a geospatial planning expert. Analyze this item and suggest the optimal H3 hexagonal resolution (0-15) for organizing it.
38
+
39
+ H3 Resolution Guide:
40
+ - Resolution 0-2: Continental/country scale (thousands of km)
41
+ - Resolution 3-4: Regional/state scale (hundreds of km)
42
+ - Resolution 5-6: Metropolitan/city scale (tens of km)
43
+ - Resolution 7-8: District/neighborhood scale (km)
44
+ - Resolution 9-10: Block/street scale (hundreds of meters)
45
+ - Resolution 11-12: Building/lot scale (tens of meters)
46
+ - Resolution 13-15: Room/precise scale (meters)
47
+
48
+ Consider:
49
+ 1. Geographic scope mentioned in the item
50
+ 2. Number of potential participants/stakeholders
51
+ 3. Type of activity (local vs regional)
52
+ 4. Resource requirements and logistics
53
+ 5. Similar projects' typical scale
54
+
55
+ ${currentResolution !== null ? `Current resolution: ${currentResolution}` : ''}
56
+ ${context ? `Context:\n${JSON.stringify(context, null, 2)}` : ''}
57
+
58
+ Return JSON:
59
+ {
60
+ "recommendedResolution": n,
61
+ "reasoning": "why this resolution",
62
+ "alternativeResolutions": [{"resolution": n, "useCase": "when to use"}],
63
+ "geographicScope": "description of area covered",
64
+ "scaleSuggestions": {
65
+ "expansion": {"resolution": n, "reason": "when to expand"},
66
+ "contraction": {"resolution": n, "reason": "when to focus"}
67
+ }
68
+ }`;
69
+
70
+ return this.llm.getJSON(
71
+ systemPrompt,
72
+ JSON.stringify(item, null, 2),
73
+ { temperature: 0.3 }
74
+ );
75
+ }
76
+
77
+ /**
78
+ * Analyze geographic distribution of data across H3 cells
79
+ * @param {string} holonId - Parent holon to analyze
80
+ * @param {string} lensName - Lens to analyze
81
+ * @param {Object} options - Options
82
+ * @returns {Promise<Object>} Distribution analysis
83
+ */
84
+ async analyzeDistribution(holonId, lensName, options = {}) {
85
+ if (!this.holosphere) {
86
+ throw new Error('HoloSphere instance required');
87
+ }
88
+
89
+ const { includeChildren = true, maxChildren = 20 } = options;
90
+
91
+ // Get data from the holon
92
+ const parentData = await this.holosphere.getAll(holonId, lensName);
93
+
94
+ // Get data from children if requested
95
+ const childrenData = {};
96
+ if (includeChildren && h3.isValidCell(holonId)) {
97
+ const resolution = h3.getResolution(holonId);
98
+ if (resolution < 15) {
99
+ const children = h3.cellToChildren(holonId, resolution + 1);
100
+ for (const child of children.slice(0, maxChildren)) {
101
+ try {
102
+ const data = await this.holosphere.getAll(child, lensName);
103
+ if (data.length > 0) {
104
+ childrenData[child] = {
105
+ count: data.length,
106
+ sample: data.slice(0, 3),
107
+ };
108
+ }
109
+ } catch {
110
+ // Skip unavailable children
111
+ }
112
+ }
113
+ }
114
+ }
115
+
116
+ const systemPrompt = `You are a geospatial analyst. Analyze the distribution of data across this H3 hexagonal region.
117
+
118
+ Parent holon: ${holonId}
119
+ Resolution: ${h3.isValidCell(holonId) ? h3.getResolution(holonId) : 'N/A'}
120
+ Parent data count: ${parentData.length}
121
+ Children with data: ${Object.keys(childrenData).length}
122
+
123
+ Analyze:
124
+ 1. Data density and distribution patterns
125
+ 2. Geographic hotspots and cold spots
126
+ 3. Coverage gaps
127
+ 4. Clustering patterns
128
+ 5. Recommendations for better coverage
129
+
130
+ Return JSON:
131
+ {
132
+ "distribution": {
133
+ "pattern": "clustered|uniform|sparse|concentrated",
134
+ "density": "high|medium|low",
135
+ "coverage": 0.0-1.0
136
+ },
137
+ "hotspots": [{"holonId": "id", "reason": "why"}],
138
+ "gaps": [{"description": "gap description", "suggestedAction": "what to do"}],
139
+ "clusters": [{"theme": "cluster theme", "holons": ["id1"]}],
140
+ "recommendations": ["rec1"],
141
+ "summary": "overview"
142
+ }`;
143
+
144
+ const userMessage = `Parent data sample:
145
+ ${JSON.stringify(parentData.slice(0, 10), null, 2)}
146
+
147
+ Children distribution:
148
+ ${JSON.stringify(childrenData, null, 2)}`;
149
+
150
+ const analysis = await this.llm.getJSON(systemPrompt, userMessage, { temperature: 0.3 });
151
+
152
+ return {
153
+ holonId,
154
+ lensName,
155
+ resolution: h3.isValidCell(holonId) ? h3.getResolution(holonId) : null,
156
+ parentDataCount: parentData.length,
157
+ childrenAnalyzed: Object.keys(childrenData).length,
158
+ analysis,
159
+ timestamp: Date.now(),
160
+ };
161
+ }
162
+
163
+ /**
164
+ * Find relevant data from neighboring H3 cells
165
+ * @param {string} holonId - Center holon
166
+ * @param {string} lensName - Lens to search
167
+ * @param {Object} options - Options
168
+ * @returns {Promise<Object>} Neighbor recommendations
169
+ */
170
+ async findNeighborRelevance(holonId, lensName, options = {}) {
171
+ if (!this.holosphere) {
172
+ throw new Error('HoloSphere instance required');
173
+ }
174
+
175
+ if (!h3.isValidCell(holonId)) {
176
+ throw new Error('Invalid H3 cell');
177
+ }
178
+
179
+ const { ringSize = 1, relevanceThreshold = 0.5 } = options;
180
+
181
+ // Get center data
182
+ const centerData = await this.holosphere.getAll(holonId, lensName);
183
+
184
+ // Get neighbor data
185
+ const neighbors = h3.gridDisk(holonId, ringSize).filter(n => n !== holonId);
186
+ const neighborData = {};
187
+
188
+ for (const neighbor of neighbors) {
189
+ try {
190
+ const data = await this.holosphere.getAll(neighbor, lensName);
191
+ if (data.length > 0) {
192
+ neighborData[neighbor] = data.slice(0, 5);
193
+ }
194
+ } catch {
195
+ // Skip
196
+ }
197
+ }
198
+
199
+ if (Object.keys(neighborData).length === 0) {
200
+ return {
201
+ holonId,
202
+ neighbors: [],
203
+ message: 'No data found in neighboring cells',
204
+ };
205
+ }
206
+
207
+ const systemPrompt = `You are a geospatial relevance analyst. Find items in neighboring H3 cells that are relevant to the center cell's data.
208
+
209
+ Center cell: ${holonId}
210
+ Ring size: ${ringSize} (immediate neighbors)
211
+
212
+ Consider:
213
+ 1. Thematic overlap
214
+ 2. Potential collaborations
215
+ 3. Shared resources
216
+ 4. Cross-boundary projects
217
+ 5. Geographic continuity of activities
218
+
219
+ Return JSON:
220
+ {
221
+ "relevantNeighbors": [
222
+ {
223
+ "holonId": "neighbor_id",
224
+ "relevanceScore": 0.0-1.0,
225
+ "relevantItems": [{"id": "item_id", "reason": "why relevant"}],
226
+ "collaborationPotential": "description"
227
+ }
228
+ ],
229
+ "crossBoundaryOpportunities": [
230
+ {
231
+ "description": "opportunity",
232
+ "involvedHolons": ["id1", "id2"],
233
+ "suggestedAction": "what to do"
234
+ }
235
+ ],
236
+ "geographicPatterns": ["pattern1"],
237
+ "summary": "overview"
238
+ }`;
239
+
240
+ const userMessage = `Center cell data:
241
+ ${JSON.stringify(centerData.slice(0, 10), null, 2)}
242
+
243
+ Neighbor data:
244
+ ${JSON.stringify(neighborData, null, 2)}`;
245
+
246
+ const analysis = await this.llm.getJSON(systemPrompt, userMessage, { temperature: 0.4 });
247
+
248
+ // Filter by threshold
249
+ if (analysis.relevantNeighbors) {
250
+ analysis.relevantNeighbors = analysis.relevantNeighbors
251
+ .filter(n => n.relevanceScore >= relevanceThreshold);
252
+ }
253
+
254
+ return {
255
+ holonId,
256
+ lensName,
257
+ ringSize,
258
+ neighborsAnalyzed: Object.keys(neighborData).length,
259
+ analysis,
260
+ timestamp: Date.now(),
261
+ };
262
+ }
263
+
264
+ /**
265
+ * Suggest geographic expansion or contraction for an item
266
+ * @param {Object} item - Item to analyze
267
+ * @param {string} currentHolonId - Current holon
268
+ * @param {string} lensName - Lens name
269
+ * @param {Object} options - Options
270
+ * @returns {Promise<Object>} Expansion/contraction suggestions
271
+ */
272
+ async suggestGeographicScope(item, currentHolonId, lensName, options = {}) {
273
+ if (!this.holosphere) {
274
+ throw new Error('HoloSphere instance required');
275
+ }
276
+
277
+ if (!h3.isValidCell(currentHolonId)) {
278
+ throw new Error('Invalid H3 cell');
279
+ }
280
+
281
+ const currentResolution = h3.getResolution(currentHolonId);
282
+
283
+ // Get parent data
284
+ let parentData = [];
285
+ if (currentResolution > 0) {
286
+ const parent = h3.cellToParent(currentHolonId, currentResolution - 1);
287
+ try {
288
+ parentData = await this.holosphere.getAll(parent, lensName);
289
+ } catch {
290
+ // No parent data
291
+ }
292
+ }
293
+
294
+ // Get sample children data
295
+ let childrenData = [];
296
+ if (currentResolution < 15) {
297
+ const children = h3.cellToChildren(currentHolonId, currentResolution + 1);
298
+ for (const child of children.slice(0, 7)) {
299
+ try {
300
+ const data = await this.holosphere.getAll(child, lensName);
301
+ childrenData.push(...data.slice(0, 2));
302
+ } catch {
303
+ // Skip
304
+ }
305
+ }
306
+ }
307
+
308
+ const systemPrompt = `You are a geographic scope advisor. Analyze whether this item should expand to a larger region (parent holon) or focus on smaller sub-regions (children holons).
309
+
310
+ Current holon: ${currentHolonId}
311
+ Current resolution: ${currentResolution}
312
+
313
+ Factors to consider:
314
+ 1. Item's stated scope and goals
315
+ 2. Current participation/activity level
316
+ 3. Resource requirements
317
+ 4. Similar activities in parent/children
318
+ 5. Natural geographic boundaries
319
+
320
+ Return JSON:
321
+ {
322
+ "currentScopeAssessment": {
323
+ "appropriate": true/false,
324
+ "reasoning": "why"
325
+ },
326
+ "expansionRecommendation": {
327
+ "recommended": true/false,
328
+ "targetResolution": n,
329
+ "reasoning": "why expand",
330
+ "benefits": ["benefit1"],
331
+ "risks": ["risk1"]
332
+ },
333
+ "contractionRecommendation": {
334
+ "recommended": true/false,
335
+ "targetResolution": n,
336
+ "reasoning": "why contract",
337
+ "suggestedFocusAreas": ["description"],
338
+ "benefits": ["benefit1"],
339
+ "risks": ["risk1"]
340
+ },
341
+ "optimalAction": "expand|contract|maintain",
342
+ "summary": "recommendation summary"
343
+ }`;
344
+
345
+ const userMessage = `Item to analyze:
346
+ ${JSON.stringify(item, null, 2)}
347
+
348
+ Parent region data (${parentData.length} items):
349
+ ${JSON.stringify(parentData.slice(0, 5), null, 2)}
350
+
351
+ Children regions data (${childrenData.length} items):
352
+ ${JSON.stringify(childrenData.slice(0, 5), null, 2)}`;
353
+
354
+ return this.llm.getJSON(systemPrompt, userMessage, { temperature: 0.3 });
355
+ }
356
+
357
+ /**
358
+ * Analyze coverage gaps in a region
359
+ * @param {string} holonId - Region to analyze
360
+ * @param {string} lensName - Lens
361
+ * @param {Object} options - Options
362
+ * @returns {Promise<Object>} Coverage analysis
363
+ */
364
+ async analyzeCoverage(holonId, lensName, options = {}) {
365
+ if (!this.holosphere) {
366
+ throw new Error('HoloSphere instance required');
367
+ }
368
+
369
+ if (!h3.isValidCell(holonId)) {
370
+ throw new Error('Invalid H3 cell');
371
+ }
372
+
373
+ const { targetResolution = null } = options;
374
+ const currentResolution = h3.getResolution(holonId);
375
+ const childResolution = targetResolution || Math.min(currentResolution + 1, 15);
376
+
377
+ // Get all children at target resolution
378
+ const children = h3.cellToChildren(holonId, childResolution);
379
+
380
+ // Check which children have data
381
+ const coverage = {
382
+ total: children.length,
383
+ withData: 0,
384
+ withoutData: 0,
385
+ dataDistribution: {},
386
+ };
387
+
388
+ const childrenWithData = [];
389
+ const childrenWithoutData = [];
390
+
391
+ for (const child of children) {
392
+ try {
393
+ const data = await this.holosphere.getAll(child, lensName);
394
+ if (data.length > 0) {
395
+ coverage.withData++;
396
+ coverage.dataDistribution[child] = data.length;
397
+ childrenWithData.push({ holon: child, count: data.length });
398
+ } else {
399
+ coverage.withoutData++;
400
+ childrenWithoutData.push(child);
401
+ }
402
+ } catch {
403
+ coverage.withoutData++;
404
+ childrenWithoutData.push(child);
405
+ }
406
+ }
407
+
408
+ const systemPrompt = `You are a geographic coverage analyst. Analyze the data coverage in this H3 region.
409
+
410
+ Region: ${holonId}
411
+ Resolution: ${currentResolution}
412
+ Child resolution analyzed: ${childResolution}
413
+ Total children: ${coverage.total}
414
+ Children with data: ${coverage.withData}
415
+ Children without data: ${coverage.withoutData}
416
+ Coverage ratio: ${(coverage.withData / coverage.total * 100).toFixed(1)}%
417
+
418
+ Analyze:
419
+ 1. Coverage patterns
420
+ 2. Potential reasons for gaps
421
+ 3. Priority areas for expansion
422
+ 4. Whether gaps are concerning or expected
423
+
424
+ Return JSON:
425
+ {
426
+ "coverageScore": 0.0-1.0,
427
+ "coverageQuality": "excellent|good|moderate|poor|minimal",
428
+ "patterns": {
429
+ "type": "clustered|scattered|peripheral|central|uniform",
430
+ "description": "pattern description"
431
+ },
432
+ "gaps": {
433
+ "count": n,
434
+ "significance": "high|medium|low",
435
+ "likelyReasons": ["reason1"],
436
+ "priorityAreas": ["description of areas to focus"]
437
+ },
438
+ "recommendations": [
439
+ {
440
+ "action": "what to do",
441
+ "priority": "high|medium|low",
442
+ "targetArea": "description"
443
+ }
444
+ ],
445
+ "summary": "overview"
446
+ }`;
447
+
448
+ const analysis = await this.llm.getJSON(systemPrompt, '', { temperature: 0.3 });
449
+
450
+ return {
451
+ holonId,
452
+ lensName,
453
+ resolution: currentResolution,
454
+ childResolution,
455
+ coverage: {
456
+ ...coverage,
457
+ ratio: coverage.withData / coverage.total,
458
+ },
459
+ childrenWithData: childrenWithData.slice(0, 10),
460
+ gapCount: childrenWithoutData.length,
461
+ analysis,
462
+ timestamp: Date.now(),
463
+ };
464
+ }
465
+
466
+ /**
467
+ * Find patterns across multiple H3 resolutions
468
+ * @param {string} holonId - Starting holon
469
+ * @param {string} lensName - Lens
470
+ * @param {Object} options - Options
471
+ * @returns {Promise<Object>} Cross-resolution insights
472
+ */
473
+ async crossResolutionInsights(holonId, lensName, options = {}) {
474
+ if (!this.holosphere) {
475
+ throw new Error('HoloSphere instance required');
476
+ }
477
+
478
+ if (!h3.isValidCell(holonId)) {
479
+ throw new Error('Invalid H3 cell');
480
+ }
481
+
482
+ const { levels = 3 } = options;
483
+ const currentResolution = h3.getResolution(holonId);
484
+
485
+ // Collect data at multiple resolutions
486
+ const resolutionData = {};
487
+
488
+ // Current level
489
+ const currentData = await this.holosphere.getAll(holonId, lensName);
490
+ resolutionData[currentResolution] = {
491
+ holon: holonId,
492
+ count: currentData.length,
493
+ sample: currentData.slice(0, 5),
494
+ };
495
+
496
+ // Parent levels
497
+ let parentHolon = holonId;
498
+ for (let i = 0; i < levels && h3.getResolution(parentHolon) > 0; i++) {
499
+ const res = h3.getResolution(parentHolon) - 1;
500
+ parentHolon = h3.cellToParent(parentHolon, res);
501
+ try {
502
+ const data = await this.holosphere.getAll(parentHolon, lensName);
503
+ resolutionData[res] = {
504
+ holon: parentHolon,
505
+ count: data.length,
506
+ sample: data.slice(0, 5),
507
+ };
508
+ } catch {
509
+ // Skip
510
+ }
511
+ }
512
+
513
+ // Child level (one level down)
514
+ if (currentResolution < 15) {
515
+ const childRes = currentResolution + 1;
516
+ const children = h3.cellToChildren(holonId, childRes);
517
+ let childTotal = 0;
518
+ const childSamples = [];
519
+
520
+ for (const child of children.slice(0, 7)) {
521
+ try {
522
+ const data = await this.holosphere.getAll(child, lensName);
523
+ childTotal += data.length;
524
+ if (data.length > 0) {
525
+ childSamples.push(...data.slice(0, 2));
526
+ }
527
+ } catch {
528
+ // Skip
529
+ }
530
+ }
531
+
532
+ resolutionData[childRes] = {
533
+ holon: `${children.length} children`,
534
+ count: childTotal,
535
+ sample: childSamples.slice(0, 5),
536
+ };
537
+ }
538
+
539
+ const systemPrompt = `You are a multi-scale geographic analyst. Find patterns that emerge across different H3 resolutions.
540
+
541
+ Starting holon: ${holonId}
542
+ Resolutions analyzed: ${Object.keys(resolutionData).sort().join(', ')}
543
+
544
+ Data at each resolution:
545
+ ${JSON.stringify(resolutionData, null, 2)}
546
+
547
+ Analyze:
548
+ 1. How themes evolve across scales
549
+ 2. What appears only at certain resolutions
550
+ 3. Aggregation patterns (local vs regional)
551
+ 4. Scale-dependent opportunities
552
+ 5. Optimal resolution for different activities
553
+
554
+ Return JSON:
555
+ {
556
+ "scalePatterns": [
557
+ {
558
+ "pattern": "description",
559
+ "visibleAt": [resolution_numbers],
560
+ "significance": "why this matters"
561
+ }
562
+ ],
563
+ "themeEvolution": {
564
+ "localThemes": ["themes at fine resolution"],
565
+ "regionalThemes": ["themes at coarse resolution"],
566
+ "consistentThemes": ["themes across all scales"]
567
+ },
568
+ "optimalResolutions": {
569
+ "forCollaboration": n,
570
+ "forResources": n,
571
+ "forCommunity": n,
572
+ "reasoning": "why"
573
+ },
574
+ "insights": ["insight1"],
575
+ "recommendations": ["rec1"],
576
+ "summary": "overview"
577
+ }`;
578
+
579
+ return this.llm.getJSON(systemPrompt, '', { temperature: 0.4 });
580
+ }
581
+
582
+ /**
583
+ * Suggest item migration between holons based on geographic fit
584
+ * @param {Object} item - Item to analyze
585
+ * @param {string} currentHolonId - Current location
586
+ * @param {string} lensName - Lens
587
+ * @param {Object} options - Options
588
+ * @returns {Promise<Object>} Migration suggestions
589
+ */
590
+ async suggestMigration(item, currentHolonId, lensName, options = {}) {
591
+ if (!this.holosphere) {
592
+ throw new Error('HoloSphere instance required');
593
+ }
594
+
595
+ if (!h3.isValidCell(currentHolonId)) {
596
+ throw new Error('Invalid H3 cell');
597
+ }
598
+
599
+ const { searchRadius = 2 } = options;
600
+
601
+ // Get data from nearby cells
602
+ const nearby = h3.gridDisk(currentHolonId, searchRadius);
603
+ const nearbyData = {};
604
+
605
+ for (const cell of nearby) {
606
+ if (cell === currentHolonId) continue;
607
+ try {
608
+ const data = await this.holosphere.getAll(cell, lensName);
609
+ if (data.length > 0) {
610
+ nearbyData[cell] = {
611
+ count: data.length,
612
+ sample: data.slice(0, 3),
613
+ };
614
+ }
615
+ } catch {
616
+ // Skip
617
+ }
618
+ }
619
+
620
+ // Also check parent
621
+ const currentRes = h3.getResolution(currentHolonId);
622
+ let parentData = null;
623
+ if (currentRes > 0) {
624
+ const parent = h3.cellToParent(currentHolonId, currentRes - 1);
625
+ try {
626
+ const data = await this.holosphere.getAll(parent, lensName);
627
+ parentData = { holon: parent, count: data.length, sample: data.slice(0, 3) };
628
+ } catch {
629
+ // Skip
630
+ }
631
+ }
632
+
633
+ const systemPrompt = `You are a geographic placement advisor. Determine if this item would be better suited in a different H3 cell.
634
+
635
+ Current location: ${currentHolonId}
636
+ Current resolution: ${currentRes}
637
+
638
+ Consider:
639
+ 1. Thematic fit with existing data in each cell
640
+ 2. Geographic scope of the item
641
+ 3. Collaboration opportunities
642
+ 4. Resource proximity
643
+ 5. Community alignment
644
+
645
+ Return JSON:
646
+ {
647
+ "currentFit": {
648
+ "score": 0.0-1.0,
649
+ "reasoning": "why current location is/isn't appropriate"
650
+ },
651
+ "migrationRecommended": true/false,
652
+ "suggestedDestinations": [
653
+ {
654
+ "holonId": "cell_id",
655
+ "fitScore": 0.0-1.0,
656
+ "reasoning": "why this location",
657
+ "benefits": ["benefit1"],
658
+ "drawbacks": ["drawback1"]
659
+ }
660
+ ],
661
+ "stayReasons": ["reason to stay if applicable"],
662
+ "moveReasons": ["reason to move if applicable"],
663
+ "recommendation": "stay|move|duplicate",
664
+ "summary": "final recommendation"
665
+ }`;
666
+
667
+ const userMessage = `Item to place:
668
+ ${JSON.stringify(item, null, 2)}
669
+
670
+ Nearby cells with data:
671
+ ${JSON.stringify(nearbyData, null, 2)}
672
+
673
+ ${parentData ? `Parent cell data:\n${JSON.stringify(parentData, null, 2)}` : ''}`;
674
+
675
+ return this.llm.getJSON(systemPrompt, userMessage, { temperature: 0.3 });
676
+ }
677
+
678
+ /**
679
+ * Generate a geographic activity report for a region
680
+ * @param {string} holonId - Region to report on
681
+ * @param {Object} options - Options
682
+ * @returns {Promise<Object>} Geographic report
683
+ */
684
+ async generateGeographicReport(holonId, options = {}) {
685
+ if (!this.holosphere) {
686
+ throw new Error('HoloSphere instance required');
687
+ }
688
+
689
+ const { lenses = ['projects', 'quests', 'events', 'resources', 'default'] } = options;
690
+
691
+ // Collect data from all lenses
692
+ const lensData = {};
693
+ for (const lens of lenses) {
694
+ try {
695
+ const data = await this.holosphere.getAll(holonId, lens);
696
+ if (data.length > 0) {
697
+ lensData[lens] = {
698
+ count: data.length,
699
+ sample: data.slice(0, 5),
700
+ };
701
+ }
702
+ } catch {
703
+ // Skip
704
+ }
705
+ }
706
+
707
+ // Get geographic context
708
+ let geoContext = {};
709
+ if (h3.isValidCell(holonId)) {
710
+ const res = h3.getResolution(holonId);
711
+ const [lat, lng] = h3.cellToLatLng(holonId);
712
+ const boundary = h3.cellToBoundary(holonId);
713
+ const area = h3.cellArea(holonId, 'km2');
714
+
715
+ geoContext = {
716
+ resolution: res,
717
+ center: { lat, lng },
718
+ areaKm2: area,
719
+ vertexCount: boundary.length,
720
+ };
721
+ }
722
+
723
+ const systemPrompt = `You are a regional activity reporter. Generate a comprehensive report on geographic activity in this H3 region.
724
+
725
+ Region: ${holonId}
726
+ Geographic context: ${JSON.stringify(geoContext, null, 2)}
727
+ Data available in ${Object.keys(lensData).length} categories
728
+
729
+ Generate a report covering:
730
+ 1. Executive summary
731
+ 2. Activity overview by category
732
+ 3. Key highlights and achievements
733
+ 4. Geographic patterns
734
+ 5. Opportunities and challenges
735
+ 6. Recommendations
736
+
737
+ Return JSON:
738
+ {
739
+ "title": "Region Report Title",
740
+ "executiveSummary": "2-3 sentence overview",
741
+ "activityOverview": {
742
+ "totalItems": n,
743
+ "categorySummaries": {"lens": "summary"}
744
+ },
745
+ "highlights": [
746
+ {"title": "highlight", "description": "details", "category": "lens"}
747
+ ],
748
+ "geographicPatterns": ["pattern1"],
749
+ "strengths": ["strength1"],
750
+ "challenges": ["challenge1"],
751
+ "opportunities": ["opportunity1"],
752
+ "recommendations": [
753
+ {"priority": "high|medium|low", "action": "what to do", "rationale": "why"}
754
+ ],
755
+ "metrics": {
756
+ "activityLevel": "high|medium|low",
757
+ "diversity": "high|medium|low",
758
+ "growth": "growing|stable|declining"
759
+ }
760
+ }`;
761
+
762
+ const report = await this.llm.getJSON(
763
+ systemPrompt,
764
+ JSON.stringify(lensData, null, 2),
765
+ { temperature: 0.4, maxTokens: 2000 }
766
+ );
767
+
768
+ return {
769
+ holonId,
770
+ geoContext,
771
+ lensesAnalyzed: Object.keys(lensData),
772
+ totalItems: Object.values(lensData).reduce((sum, l) => sum + l.count, 0),
773
+ report,
774
+ timestamp: Date.now(),
775
+ };
776
+ }
777
+
778
+ /**
779
+ * Find geographic clusters of related items
780
+ * @param {string} holonId - Region to analyze
781
+ * @param {string} lensName - Lens
782
+ * @param {Object} options - Options
783
+ * @returns {Promise<Object>} Geographic clusters
784
+ */
785
+ async findGeographicClusters(holonId, lensName, options = {}) {
786
+ if (!this.holosphere) {
787
+ throw new Error('HoloSphere instance required');
788
+ }
789
+
790
+ if (!h3.isValidCell(holonId)) {
791
+ throw new Error('Invalid H3 cell');
792
+ }
793
+
794
+ const { clusterResolution = null } = options;
795
+ const currentRes = h3.getResolution(holonId);
796
+ const targetRes = clusterResolution || Math.min(currentRes + 1, 15);
797
+
798
+ // Get data from children at cluster resolution
799
+ const children = h3.cellToChildren(holonId, targetRes);
800
+ const cellData = {};
801
+
802
+ for (const child of children) {
803
+ try {
804
+ const data = await this.holosphere.getAll(child, lensName);
805
+ if (data.length > 0) {
806
+ cellData[child] = data;
807
+ }
808
+ } catch {
809
+ // Skip
810
+ }
811
+ }
812
+
813
+ if (Object.keys(cellData).length < 2) {
814
+ return {
815
+ holonId,
816
+ clusters: [],
817
+ message: 'Not enough data for clustering',
818
+ };
819
+ }
820
+
821
+ const systemPrompt = `You are a geographic clustering expert. Find thematic clusters in this spatial data.
822
+
823
+ Region: ${holonId}
824
+ Resolution analyzed: ${targetRes}
825
+ Cells with data: ${Object.keys(cellData).length}
826
+
827
+ Identify:
828
+ 1. Thematic clusters (cells with similar content)
829
+ 2. Activity clusters (cells with related activities)
830
+ 3. Isolated cells (unique content)
831
+ 4. Potential connections between clusters
832
+
833
+ Return JSON:
834
+ {
835
+ "clusters": [
836
+ {
837
+ "name": "cluster name",
838
+ "theme": "what unifies this cluster",
839
+ "cells": ["cell_id1", "cell_id2"],
840
+ "strength": 0.0-1.0,
841
+ "characteristics": ["char1"]
842
+ }
843
+ ],
844
+ "isolatedCells": [
845
+ {"cellId": "id", "uniqueAspect": "what makes it unique"}
846
+ ],
847
+ "interClusterConnections": [
848
+ {"cluster1": "name", "cluster2": "name", "connection": "how related"}
849
+ ],
850
+ "spatialPatterns": ["pattern1"],
851
+ "recommendations": ["rec1"]
852
+ }`;
853
+
854
+ const analysis = await this.llm.getJSON(
855
+ systemPrompt,
856
+ JSON.stringify(cellData, null, 2),
857
+ { temperature: 0.4 }
858
+ );
859
+
860
+ return {
861
+ holonId,
862
+ lensName,
863
+ clusterResolution: targetRes,
864
+ cellsAnalyzed: Object.keys(cellData).length,
865
+ analysis,
866
+ timestamp: Date.now(),
867
+ };
868
+ }
869
+
870
+ /**
871
+ * Calculate and explain geographic impact of an item
872
+ * @param {Object} item - Item to analyze
873
+ * @param {string} holonId - Item's holon
874
+ * @param {string} lensName - Lens
875
+ * @param {Object} options - Options
876
+ * @returns {Promise<Object>} Impact analysis
877
+ */
878
+ async analyzeGeographicImpact(item, holonId, lensName, options = {}) {
879
+ if (!h3.isValidCell(holonId)) {
880
+ throw new Error('Invalid H3 cell');
881
+ }
882
+
883
+ const res = h3.getResolution(holonId);
884
+ const [lat, lng] = h3.cellToLatLng(holonId);
885
+ const area = h3.cellArea(holonId, 'km2');
886
+
887
+ // Get parent info for context
888
+ let parentContext = null;
889
+ if (res > 0) {
890
+ const parent = h3.cellToParent(holonId, Math.max(0, res - 2));
891
+ const parentArea = h3.cellArea(parent, 'km2');
892
+ parentContext = {
893
+ holon: parent,
894
+ resolution: res - 2,
895
+ areaKm2: parentArea,
896
+ };
897
+ }
898
+
899
+ const systemPrompt = `You are a geographic impact analyst. Analyze the geographic reach and impact of this item.
900
+
901
+ Item location: ${holonId}
902
+ Resolution: ${res}
903
+ Center: ${lat.toFixed(4)}, ${lng.toFixed(4)}
904
+ Area: ${area.toFixed(2)} km²
905
+
906
+ ${parentContext ? `Broader region: ${parentContext.areaKm2.toFixed(2)} km²` : ''}
907
+
908
+ Analyze:
909
+ 1. Direct impact area (immediate cell)
910
+ 2. Indirect impact area (spillover effects)
911
+ 3. Potential reach (if expanded)
912
+ 4. Geographic barriers/enablers
913
+ 5. Network effects
914
+
915
+ Return JSON:
916
+ {
917
+ "directImpact": {
918
+ "areaKm2": n,
919
+ "description": "direct impact area",
920
+ "affectedPopulation": "estimate or N/A"
921
+ },
922
+ "indirectImpact": {
923
+ "estimatedReach": n,
924
+ "mechanisms": ["how impact spreads"],
925
+ "neighboringAreas": ["affected areas"]
926
+ },
927
+ "potentialReach": {
928
+ "ifExpanded": {
929
+ "maxAreaKm2": n,
930
+ "optimalResolution": n,
931
+ "limitingFactors": ["factor1"]
932
+ }
933
+ },
934
+ "geographicFactors": {
935
+ "enablers": ["what helps geographic spread"],
936
+ "barriers": ["what limits spread"]
937
+ },
938
+ "impactScore": {
939
+ "local": 0.0-1.0,
940
+ "regional": 0.0-1.0,
941
+ "network": 0.0-1.0
942
+ },
943
+ "recommendations": ["how to increase impact"],
944
+ "summary": "impact overview"
945
+ }`;
946
+
947
+ return this.llm.getJSON(
948
+ systemPrompt,
949
+ JSON.stringify(item, null, 2),
950
+ { temperature: 0.3 }
951
+ );
952
+ }
953
+ }
954
+
955
+ export default H3AI;