holosphere 1.1.20 → 2.0.0-alpha1
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/.env.example +36 -0
- package/.eslintrc.json +16 -0
- package/.prettierrc.json +7 -0
- package/LICENSE +162 -38
- package/README.md +483 -367
- package/bin/holosphere-activitypub.js +158 -0
- package/cleanup-test-data.js +204 -0
- package/examples/demo.html +1333 -0
- package/examples/example-bot.js +197 -0
- package/package.json +47 -87
- package/scripts/check-bundle-size.js +54 -0
- package/scripts/check-quest-ids.js +77 -0
- package/scripts/import-holons.js +578 -0
- package/scripts/publish-to-relay.js +101 -0
- package/scripts/read-example.js +186 -0
- package/scripts/relay-diagnostic.js +59 -0
- package/scripts/relay-example.js +179 -0
- package/scripts/resync-to-relay.js +245 -0
- package/scripts/revert-import.js +196 -0
- package/scripts/test-hybrid-mode.js +108 -0
- package/scripts/test-local-storage.js +63 -0
- package/scripts/test-nostr-direct.js +55 -0
- package/scripts/test-read-data.js +45 -0
- package/scripts/test-write-read.js +63 -0
- package/scripts/verify-import.js +95 -0
- package/scripts/verify-relay-data.js +139 -0
- package/src/ai/aggregation.js +319 -0
- package/src/ai/breakdown.js +511 -0
- package/src/ai/classifier.js +217 -0
- package/src/ai/council.js +228 -0
- package/src/ai/embeddings.js +279 -0
- package/src/ai/federation-ai.js +324 -0
- package/src/ai/h3-ai.js +955 -0
- package/src/ai/index.js +112 -0
- package/src/ai/json-ops.js +225 -0
- package/src/ai/llm-service.js +205 -0
- package/src/ai/nl-query.js +223 -0
- package/src/ai/relationships.js +353 -0
- package/src/ai/schema-extractor.js +218 -0
- package/src/ai/spatial.js +293 -0
- package/src/ai/tts.js +194 -0
- package/src/content/social-protocols.js +168 -0
- package/src/core/holosphere.js +273 -0
- package/src/crypto/secp256k1.js +259 -0
- package/src/federation/discovery.js +334 -0
- package/src/federation/hologram.js +1042 -0
- package/src/federation/registry.js +386 -0
- package/src/hierarchical/upcast.js +110 -0
- package/src/index.js +2669 -0
- package/src/schema/validator.js +91 -0
- package/src/spatial/h3-operations.js +110 -0
- package/src/storage/backend-factory.js +125 -0
- package/src/storage/backend-interface.js +142 -0
- package/src/storage/backends/activitypub/server.js +653 -0
- package/src/storage/backends/activitypub-backend.js +272 -0
- package/src/storage/backends/gundb-backend.js +233 -0
- package/src/storage/backends/nostr-backend.js +136 -0
- package/src/storage/filesystem-storage-browser.js +41 -0
- package/src/storage/filesystem-storage.js +138 -0
- package/src/storage/global-tables.js +81 -0
- package/src/storage/gun-async.js +281 -0
- package/src/storage/gun-wrapper.js +221 -0
- package/src/storage/indexeddb-storage.js +122 -0
- package/src/storage/key-storage-simple.js +76 -0
- package/src/storage/key-storage.js +136 -0
- package/src/storage/memory-storage.js +59 -0
- package/src/storage/migration.js +338 -0
- package/src/storage/nostr-async.js +811 -0
- package/src/storage/nostr-client.js +939 -0
- package/src/storage/nostr-wrapper.js +211 -0
- package/src/storage/outbox-queue.js +208 -0
- package/src/storage/persistent-storage.js +109 -0
- package/src/storage/sync-service.js +164 -0
- package/src/subscriptions/manager.js +142 -0
- package/test-ai-real-api.js +202 -0
- package/tests/unit/ai/aggregation.test.js +295 -0
- package/tests/unit/ai/breakdown.test.js +446 -0
- package/tests/unit/ai/classifier.test.js +294 -0
- package/tests/unit/ai/council.test.js +262 -0
- package/tests/unit/ai/embeddings.test.js +384 -0
- package/tests/unit/ai/federation-ai.test.js +344 -0
- package/tests/unit/ai/h3-ai.test.js +458 -0
- package/tests/unit/ai/index.test.js +304 -0
- package/tests/unit/ai/json-ops.test.js +307 -0
- package/tests/unit/ai/llm-service.test.js +390 -0
- package/tests/unit/ai/nl-query.test.js +383 -0
- package/tests/unit/ai/relationships.test.js +311 -0
- package/tests/unit/ai/schema-extractor.test.js +384 -0
- package/tests/unit/ai/spatial.test.js +279 -0
- package/tests/unit/ai/tts.test.js +279 -0
- package/tests/unit/content.test.js +332 -0
- package/tests/unit/contract/core.test.js +88 -0
- package/tests/unit/contract/crypto.test.js +198 -0
- package/tests/unit/contract/data.test.js +223 -0
- package/tests/unit/contract/federation.test.js +181 -0
- package/tests/unit/contract/hierarchical.test.js +113 -0
- package/tests/unit/contract/schema.test.js +114 -0
- package/tests/unit/contract/social.test.js +217 -0
- package/tests/unit/contract/spatial.test.js +110 -0
- package/tests/unit/contract/subscriptions.test.js +128 -0
- package/tests/unit/contract/utils.test.js +159 -0
- package/tests/unit/core.test.js +152 -0
- package/tests/unit/crypto.test.js +328 -0
- package/tests/unit/federation.test.js +234 -0
- package/tests/unit/gun-async.test.js +252 -0
- package/tests/unit/hierarchical.test.js +399 -0
- package/tests/unit/integration/scenario-01-geographic-storage.test.js +74 -0
- package/tests/unit/integration/scenario-02-federation.test.js +76 -0
- package/tests/unit/integration/scenario-03-subscriptions.test.js +102 -0
- package/tests/unit/integration/scenario-04-validation.test.js +129 -0
- package/tests/unit/integration/scenario-05-hierarchy.test.js +125 -0
- package/tests/unit/integration/scenario-06-social.test.js +135 -0
- package/tests/unit/integration/scenario-07-persistence.test.js +130 -0
- package/tests/unit/integration/scenario-08-authorization.test.js +161 -0
- package/tests/unit/integration/scenario-09-cross-dimensional.test.js +139 -0
- package/tests/unit/integration/scenario-10-cross-holosphere-capabilities.test.js +357 -0
- package/tests/unit/integration/scenario-11-cross-holosphere-federation.test.js +410 -0
- package/tests/unit/integration/scenario-12-capability-federated-read.test.js +719 -0
- package/tests/unit/performance/benchmark.test.js +85 -0
- package/tests/unit/schema.test.js +213 -0
- package/tests/unit/spatial.test.js +158 -0
- package/tests/unit/storage.test.js +195 -0
- package/tests/unit/subscriptions.test.js +328 -0
- package/tests/unit/test-data-permanence-debug.js +197 -0
- package/tests/unit/test-data-permanence.js +340 -0
- package/tests/unit/test-key-persistence-fixed.js +148 -0
- package/tests/unit/test-key-persistence.js +172 -0
- package/tests/unit/test-relay-permanence.js +376 -0
- package/tests/unit/test-second-node.js +95 -0
- package/tests/unit/test-simple-write.js +89 -0
- package/vite.config.js +49 -0
- package/vitest.config.js +20 -0
- package/FEDERATION.md +0 -213
- package/compute.js +0 -298
- package/content.js +0 -980
- package/federation.js +0 -1234
- package/global.js +0 -736
- package/hexlib.js +0 -335
- package/hologram.js +0 -183
- package/holosphere-bundle.esm.js +0 -33256
- package/holosphere-bundle.js +0 -33287
- package/holosphere-bundle.min.js +0 -39
- package/holosphere.d.ts +0 -601
- package/holosphere.js +0 -719
- package/node.js +0 -246
- package/schema.js +0 -139
- package/utils.js +0 -302
|
@@ -0,0 +1,511 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Task Breakdown - Recursively decompose tasks/quests into subtasks
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Task Breakdown class
|
|
7
|
+
*/
|
|
8
|
+
export class TaskBreakdown {
|
|
9
|
+
/**
|
|
10
|
+
* @param {LLMService} llmService - LLM service instance
|
|
11
|
+
* @param {Object} holosphere - HoloSphere instance
|
|
12
|
+
*/
|
|
13
|
+
constructor(llmService, holosphere = null) {
|
|
14
|
+
this.llm = llmService;
|
|
15
|
+
this.holosphere = holosphere;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Set HoloSphere instance
|
|
20
|
+
* @param {Object} holosphere - HoloSphere instance
|
|
21
|
+
*/
|
|
22
|
+
setHoloSphere(holosphere) {
|
|
23
|
+
this.holosphere = holosphere;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Recursively break down a task/quest into subtasks
|
|
28
|
+
* @param {Object} item - The item to break down (must have id)
|
|
29
|
+
* @param {string} holonId - Holon where the item lives
|
|
30
|
+
* @param {string} lensName - Lens name (e.g., 'quests', 'tasks')
|
|
31
|
+
* @param {Object} options - Breakdown options
|
|
32
|
+
* @param {number} options.depth - Maximum recursion depth (default: 2)
|
|
33
|
+
* @param {number} options.stepsPerLevel - Number of subtasks per level (default: 3-5)
|
|
34
|
+
* @param {boolean} options.useContext - Look at other items for context (default: true)
|
|
35
|
+
* @param {boolean} options.storeResults - Store generated subtasks (default: true)
|
|
36
|
+
* @param {string} options.dependencyField - Field name for dependencies (default: 'dependencies')
|
|
37
|
+
* @param {string} options.parentField - Field name for parent reference (default: 'parent')
|
|
38
|
+
* @returns {Promise<Object>} Breakdown result with tree structure
|
|
39
|
+
*/
|
|
40
|
+
async breakdown(item, holonId, lensName, options = {}) {
|
|
41
|
+
if (!item || !item.id) {
|
|
42
|
+
throw new Error('Item must have an id property');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const {
|
|
46
|
+
depth = 2,
|
|
47
|
+
stepsPerLevel = { min: 3, max: 5 },
|
|
48
|
+
useContext = true,
|
|
49
|
+
storeResults = true,
|
|
50
|
+
dependencyField = 'dependencies',
|
|
51
|
+
parentField = 'parent',
|
|
52
|
+
} = options;
|
|
53
|
+
|
|
54
|
+
// Normalize stepsPerLevel
|
|
55
|
+
const steps = typeof stepsPerLevel === 'number'
|
|
56
|
+
? { min: stepsPerLevel, max: stepsPerLevel }
|
|
57
|
+
: stepsPerLevel;
|
|
58
|
+
|
|
59
|
+
// Get context from other items in the holon
|
|
60
|
+
let context = [];
|
|
61
|
+
if (useContext && this.holosphere) {
|
|
62
|
+
try {
|
|
63
|
+
const allItems = await this.holosphere.getAll(holonId, lensName);
|
|
64
|
+
context = allItems
|
|
65
|
+
.filter(i => i.id !== item.id)
|
|
66
|
+
.slice(0, 10); // Limit context items
|
|
67
|
+
} catch {
|
|
68
|
+
// Skip if can't get context
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Get schema for the lens if available
|
|
73
|
+
let schema = null;
|
|
74
|
+
if (this.holosphere) {
|
|
75
|
+
try {
|
|
76
|
+
schema = await this.holosphere.getSchema(lensName);
|
|
77
|
+
} catch {
|
|
78
|
+
// No schema available
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Perform recursive breakdown
|
|
83
|
+
const result = await this._breakdownRecursive(
|
|
84
|
+
item,
|
|
85
|
+
holonId,
|
|
86
|
+
lensName,
|
|
87
|
+
depth,
|
|
88
|
+
steps,
|
|
89
|
+
context,
|
|
90
|
+
schema,
|
|
91
|
+
dependencyField,
|
|
92
|
+
parentField,
|
|
93
|
+
storeResults,
|
|
94
|
+
0 // current depth
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
return {
|
|
98
|
+
original: item,
|
|
99
|
+
holonId,
|
|
100
|
+
lensName,
|
|
101
|
+
breakdown: result,
|
|
102
|
+
totalSubtasks: this._countSubtasks(result),
|
|
103
|
+
maxDepth: depth,
|
|
104
|
+
timestamp: Date.now(),
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Recursive breakdown implementation
|
|
110
|
+
* @private
|
|
111
|
+
*/
|
|
112
|
+
async _breakdownRecursive(
|
|
113
|
+
item,
|
|
114
|
+
holonId,
|
|
115
|
+
lensName,
|
|
116
|
+
maxDepth,
|
|
117
|
+
stepsPerLevel,
|
|
118
|
+
context,
|
|
119
|
+
schema,
|
|
120
|
+
dependencyField,
|
|
121
|
+
parentField,
|
|
122
|
+
storeResults,
|
|
123
|
+
currentDepth
|
|
124
|
+
) {
|
|
125
|
+
if (currentDepth >= maxDepth) {
|
|
126
|
+
return {
|
|
127
|
+
item,
|
|
128
|
+
children: [],
|
|
129
|
+
depth: currentDepth,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Generate subtasks for this item
|
|
134
|
+
const subtasks = await this._generateSubtasks(
|
|
135
|
+
item,
|
|
136
|
+
context,
|
|
137
|
+
schema,
|
|
138
|
+
stepsPerLevel,
|
|
139
|
+
dependencyField,
|
|
140
|
+
parentField
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
// Store subtasks if requested
|
|
144
|
+
if (storeResults && this.holosphere && subtasks.length > 0) {
|
|
145
|
+
for (const subtask of subtasks) {
|
|
146
|
+
try {
|
|
147
|
+
await this.holosphere.write(holonId, lensName, subtask);
|
|
148
|
+
} catch (err) {
|
|
149
|
+
console.warn(`Failed to store subtask ${subtask.id}:`, err.message);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Update parent item with child references if it doesn't have them
|
|
154
|
+
if (item._meta?.childTasks !== subtasks.map(s => s.id)) {
|
|
155
|
+
try {
|
|
156
|
+
await this.holosphere.update(holonId, lensName, item.id, {
|
|
157
|
+
_meta: {
|
|
158
|
+
...item._meta,
|
|
159
|
+
childTasks: subtasks.map(s => s.id),
|
|
160
|
+
breakdownTimestamp: Date.now(),
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
} catch {
|
|
164
|
+
// Skip if can't update
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Recursively break down each subtask
|
|
170
|
+
const children = [];
|
|
171
|
+
for (const subtask of subtasks) {
|
|
172
|
+
const childResult = await this._breakdownRecursive(
|
|
173
|
+
subtask,
|
|
174
|
+
holonId,
|
|
175
|
+
lensName,
|
|
176
|
+
maxDepth,
|
|
177
|
+
stepsPerLevel,
|
|
178
|
+
context,
|
|
179
|
+
schema,
|
|
180
|
+
dependencyField,
|
|
181
|
+
parentField,
|
|
182
|
+
storeResults,
|
|
183
|
+
currentDepth + 1
|
|
184
|
+
);
|
|
185
|
+
children.push(childResult);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return {
|
|
189
|
+
item,
|
|
190
|
+
children,
|
|
191
|
+
depth: currentDepth,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Generate subtasks for an item using AI
|
|
197
|
+
* @private
|
|
198
|
+
*/
|
|
199
|
+
async _generateSubtasks(item, context, schema, stepsPerLevel, dependencyField, parentField) {
|
|
200
|
+
const { min, max } = stepsPerLevel;
|
|
201
|
+
|
|
202
|
+
const systemPrompt = `You are a task decomposition expert. Break down the given task/quest into ${min}-${max} concrete, actionable subtasks.
|
|
203
|
+
|
|
204
|
+
Each subtask should:
|
|
205
|
+
1. Be a clear, specific action
|
|
206
|
+
2. Be smaller and more manageable than the parent
|
|
207
|
+
3. Together with siblings, fully accomplish the parent task
|
|
208
|
+
4. Have a unique ID (format: parentId-1, parentId-2, etc.)
|
|
209
|
+
5. Reference the parent task in the "${parentField}" field
|
|
210
|
+
6. List any dependencies on sibling tasks in "${dependencyField}" array
|
|
211
|
+
|
|
212
|
+
${schema ? `Follow this schema structure:\n${JSON.stringify(schema, null, 2)}` : ''}
|
|
213
|
+
|
|
214
|
+
${context.length > 0 ? `Consider these existing items for context and avoid duplication:\n${JSON.stringify(context.slice(0, 5), null, 2)}` : ''}
|
|
215
|
+
|
|
216
|
+
Return a JSON array of subtasks. Each subtask must have:
|
|
217
|
+
- id: unique identifier (parentId-1, parentId-2, etc.)
|
|
218
|
+
- title/name: clear action title
|
|
219
|
+
- description: what needs to be done
|
|
220
|
+
- ${parentField}: reference to parent id
|
|
221
|
+
- ${dependencyField}: array of sibling task ids this depends on (empty if first task)
|
|
222
|
+
- status: "pending"
|
|
223
|
+
- Any other relevant fields from the parent schema
|
|
224
|
+
|
|
225
|
+
Example output format:
|
|
226
|
+
[
|
|
227
|
+
{
|
|
228
|
+
"id": "parent-1",
|
|
229
|
+
"title": "First subtask",
|
|
230
|
+
"description": "Details...",
|
|
231
|
+
"${parentField}": "parentId",
|
|
232
|
+
"${dependencyField}": [],
|
|
233
|
+
"status": "pending"
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
"id": "parent-2",
|
|
237
|
+
"title": "Second subtask (depends on first)",
|
|
238
|
+
"description": "Details...",
|
|
239
|
+
"${parentField}": "parentId",
|
|
240
|
+
"${dependencyField}": ["parent-1"],
|
|
241
|
+
"status": "pending"
|
|
242
|
+
}
|
|
243
|
+
]`;
|
|
244
|
+
|
|
245
|
+
const userMessage = `Break down this task into ${min}-${max} subtasks:
|
|
246
|
+
|
|
247
|
+
${JSON.stringify(item, null, 2)}`;
|
|
248
|
+
|
|
249
|
+
try {
|
|
250
|
+
const subtasks = await this.llm.getJSON(systemPrompt, userMessage, {
|
|
251
|
+
temperature: 0.4,
|
|
252
|
+
maxTokens: 2000
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
// Ensure all subtasks have proper parent reference and IDs
|
|
256
|
+
return subtasks.map((subtask, index) => ({
|
|
257
|
+
...subtask,
|
|
258
|
+
id: subtask.id || `${item.id}-${index + 1}`,
|
|
259
|
+
[parentField]: item.id,
|
|
260
|
+
[dependencyField]: subtask[dependencyField] || [],
|
|
261
|
+
status: subtask.status || 'pending',
|
|
262
|
+
_meta: {
|
|
263
|
+
...subtask._meta,
|
|
264
|
+
generatedFrom: item.id,
|
|
265
|
+
generatedAt: Date.now(),
|
|
266
|
+
depth: (item._meta?.depth || 0) + 1,
|
|
267
|
+
}
|
|
268
|
+
}));
|
|
269
|
+
} catch (error) {
|
|
270
|
+
console.error('Failed to generate subtasks:', error);
|
|
271
|
+
return [];
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Count total subtasks in a breakdown tree
|
|
277
|
+
* @private
|
|
278
|
+
*/
|
|
279
|
+
_countSubtasks(node) {
|
|
280
|
+
if (!node.children || node.children.length === 0) {
|
|
281
|
+
return 0;
|
|
282
|
+
}
|
|
283
|
+
return node.children.length +
|
|
284
|
+
node.children.reduce((sum, child) => sum + this._countSubtasks(child), 0);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Flatten a breakdown tree into a list
|
|
289
|
+
* @param {Object} breakdownResult - Result from breakdown()
|
|
290
|
+
* @returns {Object[]} Flat list of all items
|
|
291
|
+
*/
|
|
292
|
+
flatten(breakdownResult) {
|
|
293
|
+
const items = [];
|
|
294
|
+
|
|
295
|
+
const traverse = (node) => {
|
|
296
|
+
if (node.item) {
|
|
297
|
+
items.push(node.item);
|
|
298
|
+
}
|
|
299
|
+
if (node.children) {
|
|
300
|
+
for (const child of node.children) {
|
|
301
|
+
traverse(child);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
if (breakdownResult.breakdown) {
|
|
307
|
+
traverse(breakdownResult.breakdown);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
return items;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Get dependency order for execution
|
|
315
|
+
* @param {Object} breakdownResult - Result from breakdown()
|
|
316
|
+
* @returns {Object[]} Items in dependency order (parents before children)
|
|
317
|
+
*/
|
|
318
|
+
getDependencyOrder(breakdownResult) {
|
|
319
|
+
const items = this.flatten(breakdownResult);
|
|
320
|
+
|
|
321
|
+
// Sort by depth (parents first) then by dependency order within same depth
|
|
322
|
+
return items.sort((a, b) => {
|
|
323
|
+
const depthA = a._meta?.depth || 0;
|
|
324
|
+
const depthB = b._meta?.depth || 0;
|
|
325
|
+
if (depthA !== depthB) return depthA - depthB;
|
|
326
|
+
|
|
327
|
+
// At same depth, check if b depends on a
|
|
328
|
+
const bDeps = b.dependencies || [];
|
|
329
|
+
if (bDeps.includes(a.id)) return -1;
|
|
330
|
+
|
|
331
|
+
const aDeps = a.dependencies || [];
|
|
332
|
+
if (aDeps.includes(b.id)) return 1;
|
|
333
|
+
|
|
334
|
+
return 0;
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Suggest breakdown strategy for an item
|
|
340
|
+
* @param {Object} item - Item to analyze
|
|
341
|
+
* @returns {Promise<Object>} Suggested breakdown strategy
|
|
342
|
+
*/
|
|
343
|
+
async suggestStrategy(item) {
|
|
344
|
+
const systemPrompt = `You are a task decomposition expert. Analyze this task and suggest the best breakdown strategy.
|
|
345
|
+
|
|
346
|
+
Consider:
|
|
347
|
+
1. Task complexity
|
|
348
|
+
2. Recommended depth (1-4 levels)
|
|
349
|
+
3. Recommended subtasks per level (2-7)
|
|
350
|
+
4. Key areas to focus on
|
|
351
|
+
5. Potential dependencies between subtasks
|
|
352
|
+
|
|
353
|
+
Return JSON:
|
|
354
|
+
{
|
|
355
|
+
"complexity": "simple|moderate|complex|very_complex",
|
|
356
|
+
"recommendedDepth": n,
|
|
357
|
+
"recommendedStepsPerLevel": {"min": n, "max": n},
|
|
358
|
+
"focusAreas": ["area1", "area2"],
|
|
359
|
+
"potentialChallenges": ["challenge1"],
|
|
360
|
+
"estimatedTotalSubtasks": n,
|
|
361
|
+
"reasoning": "explanation"
|
|
362
|
+
}`;
|
|
363
|
+
|
|
364
|
+
return this.llm.getJSON(
|
|
365
|
+
systemPrompt,
|
|
366
|
+
JSON.stringify(item, null, 2),
|
|
367
|
+
{ temperature: 0.3 }
|
|
368
|
+
);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Rebalance a breakdown tree (redistribute subtasks)
|
|
373
|
+
* @param {Object} breakdownResult - Existing breakdown result
|
|
374
|
+
* @param {Object} options - Rebalancing options
|
|
375
|
+
* @returns {Promise<Object>} Rebalanced breakdown
|
|
376
|
+
*/
|
|
377
|
+
async rebalance(breakdownResult, options = {}) {
|
|
378
|
+
const { targetStepsPerLevel = { min: 3, max: 5 } } = options;
|
|
379
|
+
|
|
380
|
+
const items = this.flatten(breakdownResult);
|
|
381
|
+
|
|
382
|
+
const systemPrompt = `You are a task organization expert. Rebalance these tasks to have ${targetStepsPerLevel.min}-${targetStepsPerLevel.max} items per level.
|
|
383
|
+
|
|
384
|
+
Current tasks:
|
|
385
|
+
${JSON.stringify(items, null, 2)}
|
|
386
|
+
|
|
387
|
+
Reorganize by:
|
|
388
|
+
1. Merging tasks that are too granular
|
|
389
|
+
2. Splitting tasks that are too large
|
|
390
|
+
3. Maintaining proper dependency chains
|
|
391
|
+
4. Keeping the same overall scope
|
|
392
|
+
|
|
393
|
+
Return JSON with the rebalanced structure:
|
|
394
|
+
{
|
|
395
|
+
"tasks": [...],
|
|
396
|
+
"changes": [{"type": "merge|split|move", "description": "..."}],
|
|
397
|
+
"summary": "what changed"
|
|
398
|
+
}`;
|
|
399
|
+
|
|
400
|
+
return this.llm.getJSON(systemPrompt, '', { temperature: 0.3 });
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Update progress on a breakdown tree
|
|
405
|
+
* @param {string} holonId - Holon ID
|
|
406
|
+
* @param {string} lensName - Lens name
|
|
407
|
+
* @param {string} itemId - Root item ID
|
|
408
|
+
* @returns {Promise<Object>} Progress summary
|
|
409
|
+
*/
|
|
410
|
+
async getProgress(holonId, lensName, itemId) {
|
|
411
|
+
if (!this.holosphere) {
|
|
412
|
+
throw new Error('HoloSphere instance required');
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// Get all items
|
|
416
|
+
const allItems = await this.holosphere.getAll(holonId, lensName);
|
|
417
|
+
|
|
418
|
+
// Find root and all descendants
|
|
419
|
+
const root = allItems.find(i => i.id === itemId);
|
|
420
|
+
if (!root) {
|
|
421
|
+
throw new Error(`Item ${itemId} not found`);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
const descendants = this._findDescendants(itemId, allItems);
|
|
425
|
+
|
|
426
|
+
// Calculate progress
|
|
427
|
+
const total = descendants.length;
|
|
428
|
+
const completed = descendants.filter(i =>
|
|
429
|
+
i.status === 'completed' || i.status === 'done'
|
|
430
|
+
).length;
|
|
431
|
+
const inProgress = descendants.filter(i =>
|
|
432
|
+
i.status === 'in_progress' || i.status === 'active'
|
|
433
|
+
).length;
|
|
434
|
+
const pending = total - completed - inProgress;
|
|
435
|
+
|
|
436
|
+
return {
|
|
437
|
+
itemId,
|
|
438
|
+
title: root.title || root.name,
|
|
439
|
+
total,
|
|
440
|
+
completed,
|
|
441
|
+
inProgress,
|
|
442
|
+
pending,
|
|
443
|
+
percentComplete: total > 0 ? Math.round((completed / total) * 100) : 0,
|
|
444
|
+
blockers: this._findBlockers(descendants),
|
|
445
|
+
nextUp: this._findNextTasks(descendants),
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* Find all descendants of an item
|
|
451
|
+
* @private
|
|
452
|
+
*/
|
|
453
|
+
_findDescendants(itemId, allItems, parentField = 'parent') {
|
|
454
|
+
const descendants = [];
|
|
455
|
+
const children = allItems.filter(i => i[parentField] === itemId);
|
|
456
|
+
|
|
457
|
+
for (const child of children) {
|
|
458
|
+
descendants.push(child);
|
|
459
|
+
descendants.push(...this._findDescendants(child.id, allItems, parentField));
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
return descendants;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* Find blocked tasks
|
|
467
|
+
* @private
|
|
468
|
+
*/
|
|
469
|
+
_findBlockers(items, dependencyField = 'dependencies') {
|
|
470
|
+
return items.filter(item => {
|
|
471
|
+
const deps = item[dependencyField] || [];
|
|
472
|
+
if (deps.length === 0) return false;
|
|
473
|
+
|
|
474
|
+
// Check if any dependency is not completed
|
|
475
|
+
return deps.some(depId => {
|
|
476
|
+
const dep = items.find(i => i.id === depId);
|
|
477
|
+
return dep && dep.status !== 'completed' && dep.status !== 'done';
|
|
478
|
+
});
|
|
479
|
+
}).map(item => ({
|
|
480
|
+
id: item.id,
|
|
481
|
+
title: item.title || item.name,
|
|
482
|
+
blockedBy: item[dependencyField],
|
|
483
|
+
}));
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* Find next tasks ready to work on
|
|
488
|
+
* @private
|
|
489
|
+
*/
|
|
490
|
+
_findNextTasks(items, dependencyField = 'dependencies') {
|
|
491
|
+
return items.filter(item => {
|
|
492
|
+
if (item.status === 'completed' || item.status === 'done') return false;
|
|
493
|
+
if (item.status === 'in_progress' || item.status === 'active') return false;
|
|
494
|
+
|
|
495
|
+
const deps = item[dependencyField] || [];
|
|
496
|
+
if (deps.length === 0) return true;
|
|
497
|
+
|
|
498
|
+
// All dependencies must be completed
|
|
499
|
+
return deps.every(depId => {
|
|
500
|
+
const dep = items.find(i => i.id === depId);
|
|
501
|
+
return !dep || dep.status === 'completed' || dep.status === 'done';
|
|
502
|
+
});
|
|
503
|
+
}).slice(0, 5).map(item => ({
|
|
504
|
+
id: item.id,
|
|
505
|
+
title: item.title || item.name,
|
|
506
|
+
description: item.description,
|
|
507
|
+
}));
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
export default TaskBreakdown;
|