rax-flow-core 0.1.4 → 2.0.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.
- package/dist/governance/audit-trail.d.ts +94 -0
- package/dist/governance/audit-trail.d.ts.map +1 -0
- package/dist/governance/audit-trail.js +246 -0
- package/dist/governance/audit-trail.js.map +1 -0
- package/dist/governance/policy-engine.d.ts +101 -0
- package/dist/governance/policy-engine.d.ts.map +1 -0
- package/dist/governance/policy-engine.js +446 -0
- package/dist/governance/policy-engine.js.map +1 -0
- package/dist/governance/rbac-engine.d.ts +59 -0
- package/dist/governance/rbac-engine.d.ts.map +1 -0
- package/dist/governance/rbac-engine.js +183 -0
- package/dist/governance/rbac-engine.js.map +1 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/memory/embeddings-service.d.ts +116 -0
- package/dist/memory/embeddings-service.d.ts.map +1 -0
- package/dist/memory/embeddings-service.js +287 -0
- package/dist/memory/embeddings-service.js.map +1 -0
- package/dist/memory/local-vector-store.d.ts +37 -3
- package/dist/memory/local-vector-store.d.ts.map +1 -1
- package/dist/memory/local-vector-store.js +91 -8
- package/dist/memory/local-vector-store.js.map +1 -1
- package/dist/orchestrator/core-orchestrator.d.ts +12 -0
- package/dist/orchestrator/core-orchestrator.d.ts.map +1 -1
- package/dist/orchestrator/core-orchestrator.js +75 -0
- package/dist/orchestrator/core-orchestrator.js.map +1 -1
- package/dist/orchestrator/task-decomposer.d.ts +56 -0
- package/dist/orchestrator/task-decomposer.d.ts.map +1 -0
- package/dist/orchestrator/task-decomposer.js +286 -0
- package/dist/orchestrator/task-decomposer.js.map +1 -0
- package/dist/plugins/plugin-system.d.ts +84 -1
- package/dist/plugins/plugin-system.d.ts.map +1 -1
- package/dist/plugins/plugin-system.js +91 -0
- package/dist/plugins/plugin-system.js.map +1 -1
- package/package.json +7 -7
- package/src/governance/audit-trail.ts +375 -0
- package/src/governance/policy-engine.ts +582 -0
- package/src/governance/rbac-engine.ts +244 -0
- package/src/index.ts +5 -2
- package/src/memory/embeddings-service.ts +322 -0
- package/src/memory/local-vector-store.ts +105 -8
- package/src/orchestrator/core-orchestrator.ts +78 -0
- package/src/orchestrator/task-decomposer.ts +428 -0
- package/src/plugins/plugin-system.ts +162 -1
- package/LICENSE +0 -21
- package/tsconfig.tsbuildinfo +0 -1
|
@@ -1,30 +1,62 @@
|
|
|
1
1
|
import { IVectorStore, MemoryDocument, SearchResult } from "../types/contracts.js";
|
|
2
|
-
import { readFile, writeFile, mkdir } from "node:fs/promises";
|
|
2
|
+
import { readFile, writeFile, mkdir, stat } from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
|
|
5
|
+
export interface StorageMetadata {
|
|
6
|
+
version: string;
|
|
7
|
+
createdAt: string;
|
|
8
|
+
updatedAt: string;
|
|
9
|
+
documentCount: number;
|
|
10
|
+
vectorDimensions: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
5
13
|
/**
|
|
6
14
|
* A lightweight, local vector store using Cosine Similarity.
|
|
7
15
|
* Suitable for small-to-medium datasets (thousands of records).
|
|
8
|
-
* Persists data
|
|
16
|
+
* Persists data with versioning, metadata, and TTL support.
|
|
9
17
|
*/
|
|
10
18
|
export class LocalVectorStore implements IVectorStore {
|
|
11
19
|
private documents: MemoryDocument[] = [];
|
|
12
20
|
private vectors: number[][] = [];
|
|
13
21
|
private metadata: Map<string, Record<string, unknown>> = new Map();
|
|
22
|
+
private ttlMap: Map<string, number> = new Map(); // doc id -> expiry timestamp
|
|
23
|
+
private storageMetadata: StorageMetadata = {
|
|
24
|
+
version: "1.0",
|
|
25
|
+
createdAt: new Date().toISOString(),
|
|
26
|
+
updatedAt: new Date().toISOString(),
|
|
27
|
+
documentCount: 0,
|
|
28
|
+
vectorDimensions: 0
|
|
29
|
+
};
|
|
14
30
|
|
|
15
31
|
constructor(private persistPath?: string) { }
|
|
16
32
|
|
|
17
|
-
async add(documents: MemoryDocument[], vectors: number[][]): Promise<void> {
|
|
33
|
+
async add(documents: MemoryDocument[], vectors: number[][], ttlSeconds?: number): Promise<void> {
|
|
18
34
|
if (documents.length !== vectors.length) {
|
|
19
35
|
throw new Error("Mismatched documents and vectors count.");
|
|
20
36
|
}
|
|
21
37
|
this.documents.push(...documents);
|
|
22
38
|
this.vectors.push(...vectors);
|
|
39
|
+
|
|
40
|
+
// Update metadata
|
|
41
|
+
if (vectors.length > 0) {
|
|
42
|
+
this.storageMetadata.vectorDimensions = vectors[0].length;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Set TTL if provided
|
|
46
|
+
if (ttlSeconds) {
|
|
47
|
+
const expiryTime = Date.now() + ttlSeconds * 1000;
|
|
48
|
+
documents.forEach(doc => this.ttlMap.set(doc.id, expiryTime));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
this.storageMetadata.documentCount = this.documents.length;
|
|
52
|
+
this.storageMetadata.updatedAt = new Date().toISOString();
|
|
23
53
|
if (this.persistPath) await this.save();
|
|
24
54
|
}
|
|
25
55
|
|
|
26
56
|
async search(queryVector: number[], limit: number = 5): Promise<SearchResult[]> {
|
|
27
57
|
if (this.vectors.length === 0) return [];
|
|
58
|
+
|
|
59
|
+
this.pruneExpiredDocuments();
|
|
28
60
|
|
|
29
61
|
const results: SearchResult[] = this.vectors.map((vec, i) => {
|
|
30
62
|
const score = this.cosineSimilarity(queryVector, vec);
|
|
@@ -40,6 +72,9 @@ export class LocalVectorStore implements IVectorStore {
|
|
|
40
72
|
this.documents = [];
|
|
41
73
|
this.vectors = [];
|
|
42
74
|
this.metadata.clear();
|
|
75
|
+
this.ttlMap.clear();
|
|
76
|
+
this.storageMetadata.documentCount = 0;
|
|
77
|
+
this.storageMetadata.updatedAt = new Date().toISOString();
|
|
43
78
|
if (this.persistPath) await this.save();
|
|
44
79
|
}
|
|
45
80
|
|
|
@@ -50,8 +85,11 @@ export class LocalVectorStore implements IVectorStore {
|
|
|
50
85
|
this.documents.splice(idx, 1);
|
|
51
86
|
this.vectors.splice(idx, 1);
|
|
52
87
|
this.metadata.delete(id);
|
|
88
|
+
this.ttlMap.delete(id);
|
|
53
89
|
}
|
|
54
90
|
}
|
|
91
|
+
this.storageMetadata.documentCount = this.documents.length;
|
|
92
|
+
this.storageMetadata.updatedAt = new Date().toISOString();
|
|
55
93
|
if (this.persistPath) await this.save();
|
|
56
94
|
}
|
|
57
95
|
|
|
@@ -69,10 +107,51 @@ export class LocalVectorStore implements IVectorStore {
|
|
|
69
107
|
public getMetrics() {
|
|
70
108
|
return {
|
|
71
109
|
documentCount: this.documents.length,
|
|
72
|
-
vectorDimensions: this.vectors[0]?.length || 0
|
|
110
|
+
vectorDimensions: this.vectors[0]?.length || 0,
|
|
111
|
+
storagePath: this.persistPath || "in-memory",
|
|
112
|
+
metadata: this.storageMetadata
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Export store state for external persistence or backup.
|
|
118
|
+
*/
|
|
119
|
+
public export() {
|
|
120
|
+
return {
|
|
121
|
+
documents: this.documents,
|
|
122
|
+
vectors: this.vectors,
|
|
123
|
+
metadata: Object.fromEntries(this.metadata),
|
|
124
|
+
storageMetadata: this.storageMetadata
|
|
73
125
|
};
|
|
74
126
|
}
|
|
75
127
|
|
|
128
|
+
/**
|
|
129
|
+
* Import store state from external source.
|
|
130
|
+
*/
|
|
131
|
+
public async import(state: ReturnType<LocalVectorStore['export']>): Promise<void> {
|
|
132
|
+
this.documents = state.documents;
|
|
133
|
+
this.vectors = state.vectors;
|
|
134
|
+
this.metadata = new Map(Object.entries(state.metadata));
|
|
135
|
+
this.storageMetadata = state.storageMetadata;
|
|
136
|
+
if (this.persistPath) await this.save();
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Remove expired documents based on TTL.
|
|
141
|
+
*/
|
|
142
|
+
private pruneExpiredDocuments(): void {
|
|
143
|
+
const now = Date.now();
|
|
144
|
+
const expiredIds: string[] = [];
|
|
145
|
+
|
|
146
|
+
this.ttlMap.forEach((expiry, docId) => {
|
|
147
|
+
if (expiry < now) expiredIds.push(docId);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
if (expiredIds.length > 0) {
|
|
151
|
+
this.delete(expiredIds);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
76
155
|
/**
|
|
77
156
|
* Calculates cosine similarity between two numeric vectors.
|
|
78
157
|
*/
|
|
@@ -112,18 +191,36 @@ export class LocalVectorStore implements IVectorStore {
|
|
|
112
191
|
}
|
|
113
192
|
|
|
114
193
|
/**
|
|
115
|
-
* Internal save to disk.
|
|
194
|
+
* Internal save to disk with error handling and version info.
|
|
116
195
|
*/
|
|
117
196
|
private async save(): Promise<void> {
|
|
118
197
|
if (!this.persistPath) return;
|
|
119
198
|
try {
|
|
120
199
|
await mkdir(path.dirname(this.persistPath), { recursive: true });
|
|
121
|
-
|
|
200
|
+
const payload = {
|
|
201
|
+
metadata: this.storageMetadata,
|
|
122
202
|
documents: this.documents,
|
|
123
|
-
vectors: this.vectors
|
|
124
|
-
|
|
203
|
+
vectors: this.vectors,
|
|
204
|
+
ttl: Object.fromEntries(this.ttlMap)
|
|
205
|
+
};
|
|
206
|
+
await writeFile(this.persistPath, JSON.stringify(payload, null, 2));
|
|
125
207
|
} catch (err) {
|
|
126
208
|
console.error("Failed to persist vector store:", err);
|
|
127
209
|
}
|
|
128
210
|
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Check if store is stale and needs reload.
|
|
214
|
+
*/
|
|
215
|
+
public async isStale(): Promise<boolean> {
|
|
216
|
+
if (!this.persistPath) return false;
|
|
217
|
+
try {
|
|
218
|
+
const stats = await stat(this.persistPath);
|
|
219
|
+
const fileTime = new Date(stats.mtime).getTime();
|
|
220
|
+
const storageTime = new Date(this.storageMetadata.updatedAt).getTime();
|
|
221
|
+
return fileTime > storageTime;
|
|
222
|
+
} catch {
|
|
223
|
+
return false;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
129
226
|
}
|
|
@@ -330,10 +330,31 @@ export class CoreOrchestrator {
|
|
|
330
330
|
}
|
|
331
331
|
};
|
|
332
332
|
|
|
333
|
+
// Inject memory context if available
|
|
334
|
+
if (this.memory && input.userPrompt) {
|
|
335
|
+
try {
|
|
336
|
+
const memoryContext = await this.getMemoryContext(input.userPrompt);
|
|
337
|
+
if (memoryContext) {
|
|
338
|
+
attemptInput.context!.memoryContext = memoryContext;
|
|
339
|
+
}
|
|
340
|
+
} catch (err) {
|
|
341
|
+
console.warn("Memory context retrieval failed:", err);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
333
345
|
await this.plugins.runBeforeNode({ node, input: attemptInput });
|
|
334
346
|
const output = await agent.run(attemptInput);
|
|
335
347
|
await this.plugins.runAfterNode({ node, input: attemptInput, output });
|
|
336
348
|
|
|
349
|
+
// Persist node output to memory
|
|
350
|
+
if (this.memory && output.success) {
|
|
351
|
+
try {
|
|
352
|
+
await this.persistNodeToMemory(node, output);
|
|
353
|
+
} catch (err) {
|
|
354
|
+
console.warn("Failed to persist node to memory:", err);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
337
358
|
// HITL (Human-in-the-Loop) Intercept
|
|
338
359
|
if (node.requireApproval) {
|
|
339
360
|
this.events.emit({
|
|
@@ -579,4 +600,61 @@ export class CoreOrchestrator {
|
|
|
579
600
|
};
|
|
580
601
|
}
|
|
581
602
|
}
|
|
603
|
+
|
|
604
|
+
/**
|
|
605
|
+
* Retrieve relevant context from memory based on input prompt
|
|
606
|
+
*/
|
|
607
|
+
private async getMemoryContext(prompt: string): Promise<string | undefined> {
|
|
608
|
+
if (!this.memory) return undefined;
|
|
609
|
+
|
|
610
|
+
try {
|
|
611
|
+
const context = await this.memory.getQuantumContext(this.getPromptVector(prompt), 3);
|
|
612
|
+
return context || undefined;
|
|
613
|
+
} catch (err) {
|
|
614
|
+
console.warn("Error retrieving memory context:", err);
|
|
615
|
+
return undefined;
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
/**
|
|
620
|
+
* Persist successful node execution to memory graph
|
|
621
|
+
*/
|
|
622
|
+
private async persistNodeToMemory(node: WorkflowNode, output: AgentOutput): Promise<void> {
|
|
623
|
+
if (!this.memory) return;
|
|
624
|
+
|
|
625
|
+
try {
|
|
626
|
+
const nodeContent = JSON.stringify({
|
|
627
|
+
agent: node.agent,
|
|
628
|
+
output: output.data,
|
|
629
|
+
confidence: output.confidence
|
|
630
|
+
});
|
|
631
|
+
|
|
632
|
+
const vector = this.getPromptVector(nodeContent);
|
|
633
|
+
|
|
634
|
+
await this.memory.remember({
|
|
635
|
+
id: node.id,
|
|
636
|
+
text: nodeContent,
|
|
637
|
+
metadata: {
|
|
638
|
+
agent: node.agent,
|
|
639
|
+
success: output.success,
|
|
640
|
+
confidence: output.confidence
|
|
641
|
+
},
|
|
642
|
+
timestamp: Date.now()
|
|
643
|
+
}, vector);
|
|
644
|
+
} catch (err) {
|
|
645
|
+
console.warn("Error persisting to memory:", err);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
/**
|
|
650
|
+
* Simple vector representation of prompt for memory search
|
|
651
|
+
*/
|
|
652
|
+
private getPromptVector(prompt: string): number[] {
|
|
653
|
+
const vector = new Array(384).fill(0);
|
|
654
|
+
for (let i = 0; i < prompt.length; i++) {
|
|
655
|
+
vector[i % 384] += prompt.charCodeAt(i) / 1000;
|
|
656
|
+
}
|
|
657
|
+
const magnitude = Math.sqrt(vector.reduce((sum, v) => sum + v * v, 0));
|
|
658
|
+
return magnitude > 0 ? vector.map(v => v / magnitude) : vector;
|
|
659
|
+
}
|
|
582
660
|
}
|
|
@@ -0,0 +1,428 @@
|
|
|
1
|
+
import { IModelProvider, WorkflowGraph, WorkflowNode, Intent } from "../types/contracts.js";
|
|
2
|
+
import { Decomposition, Subtask } from "./decomposition-engine.js";
|
|
3
|
+
|
|
4
|
+
export interface DecompositionStrategy {
|
|
5
|
+
name: string;
|
|
6
|
+
description: string;
|
|
7
|
+
applicableIntents: Intent[];
|
|
8
|
+
maxChunkSize: number;
|
|
9
|
+
enableParallelization: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface DecompositionResult {
|
|
13
|
+
workflow: WorkflowGraph;
|
|
14
|
+
subworkflows: WorkflowGraph[];
|
|
15
|
+
strategy: string;
|
|
16
|
+
confidence: number;
|
|
17
|
+
reasoning: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface TaskAnalysis {
|
|
21
|
+
complexity: "low" | "medium" | "high" | "extreme";
|
|
22
|
+
estimatedTokens: number;
|
|
23
|
+
suggestedStrategy: string;
|
|
24
|
+
canDecompose: boolean;
|
|
25
|
+
detectedPatterns: string[];
|
|
26
|
+
riskFactors: string[];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const DEFAULT_STRATEGIES: DecompositionStrategy[] = [
|
|
30
|
+
{
|
|
31
|
+
name: "sequential",
|
|
32
|
+
description: "Linear step-by-step execution",
|
|
33
|
+
applicableIntents: ["brainstorm", "spec", "architecture", "plan"],
|
|
34
|
+
maxChunkSize: 1,
|
|
35
|
+
enableParallelization: false
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
name: "parallel_verify",
|
|
39
|
+
description: "Execute multiple verifications in parallel",
|
|
40
|
+
applicableIntents: ["test", "benchmark", "optimize"],
|
|
41
|
+
maxChunkSize: 5,
|
|
42
|
+
enableParallelization: true
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
name: "code_pipeline",
|
|
46
|
+
description: "Standard code generation pipeline",
|
|
47
|
+
applicableIntents: ["generate_code"],
|
|
48
|
+
maxChunkSize: 4,
|
|
49
|
+
enableParallelization: false
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
name: "fix_loop",
|
|
53
|
+
description: "Iterative fix with verification",
|
|
54
|
+
applicableIntents: ["fix"],
|
|
55
|
+
maxChunkSize: 2,
|
|
56
|
+
enableParallelization: false
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
name: "document_chain",
|
|
60
|
+
description: "Documentation generation chain",
|
|
61
|
+
applicableIntents: ["document"],
|
|
62
|
+
maxChunkSize: 3,
|
|
63
|
+
enableParallelization: true
|
|
64
|
+
}
|
|
65
|
+
];
|
|
66
|
+
|
|
67
|
+
const COMPLEXITY_THRESHOLDS = {
|
|
68
|
+
low: { maxWords: 20, maxConcepts: 2 },
|
|
69
|
+
medium: { maxWords: 50, maxConcepts: 4 },
|
|
70
|
+
high: { maxWords: 100, maxConcepts: 6 },
|
|
71
|
+
extreme: { maxWords: Infinity, maxConcepts: Infinity }
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const COMPLEX_PATTERNS = [
|
|
75
|
+
{ pattern: /microservice|distributed|scalab/i, weight: 3, type: "architecture" },
|
|
76
|
+
{ pattern: /auth|security|encrypt|oauth/i, weight: 2, type: "security" },
|
|
77
|
+
{ pattern: /database|persist|store|query/i, weight: 2, type: "data" },
|
|
78
|
+
{ pattern: /api|rest|graphql|endpoint/i, weight: 1, type: "api" },
|
|
79
|
+
{ pattern: /test|coverage|verify|validate/i, weight: 1, type: "testing" },
|
|
80
|
+
{ pattern: /deploy|ci\/cd|pipeline|infra/i, weight: 2, type: "devops" },
|
|
81
|
+
{ pattern: /real.?time|websocket|stream/i, weight: 2, type: "realtime" },
|
|
82
|
+
{ pattern: /ai|ml|model|neural|llm/i, weight: 2, type: "ai" }
|
|
83
|
+
];
|
|
84
|
+
|
|
85
|
+
export class TaskDecomposer {
|
|
86
|
+
private readonly basicDecomposer = new Decomposition();
|
|
87
|
+
|
|
88
|
+
constructor(
|
|
89
|
+
private provider?: IModelProvider,
|
|
90
|
+
private strategies: DecompositionStrategy[] = DEFAULT_STRATEGIES
|
|
91
|
+
) {}
|
|
92
|
+
|
|
93
|
+
async analyze(prompt: string, intent: Intent): Promise<TaskAnalysis> {
|
|
94
|
+
const wordCount = prompt.split(/\s+/).length;
|
|
95
|
+
const detectedPatterns = this.detectPatterns(prompt);
|
|
96
|
+
const complexity = this.calculateComplexity(prompt, detectedPatterns);
|
|
97
|
+
const estimatedTokens = this.estimateTokens(prompt, complexity);
|
|
98
|
+
const strategy = this.selectStrategy(intent, complexity);
|
|
99
|
+
|
|
100
|
+
const riskFactors = this.identifyRisks(prompt, complexity, detectedPatterns);
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
complexity,
|
|
104
|
+
estimatedTokens,
|
|
105
|
+
suggestedStrategy: strategy.name,
|
|
106
|
+
canDecompose: complexity !== "low" && estimatedTokens > 500,
|
|
107
|
+
detectedPatterns: detectedPatterns.map(p => p.type),
|
|
108
|
+
riskFactors
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async decompose(
|
|
113
|
+
prompt: string,
|
|
114
|
+
intent: Intent,
|
|
115
|
+
options?: {
|
|
116
|
+
useLLM?: boolean;
|
|
117
|
+
maxSubtasks?: number;
|
|
118
|
+
preferParallel?: boolean;
|
|
119
|
+
}
|
|
120
|
+
): Promise<DecompositionResult> {
|
|
121
|
+
const analysis = await this.analyze(prompt, intent);
|
|
122
|
+
const strategy = this.strategies.find(s => s.name === analysis.suggestedStrategy) ?? this.strategies[0];
|
|
123
|
+
|
|
124
|
+
if (options?.useLLM && this.provider && analysis.complexity !== "low") {
|
|
125
|
+
return this.llmDecompose(prompt, intent, strategy, analysis);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return this.heuristicDecompose(prompt, intent, strategy, analysis, options);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
private async llmDecompose(
|
|
132
|
+
prompt: string,
|
|
133
|
+
intent: Intent,
|
|
134
|
+
strategy: DecompositionStrategy,
|
|
135
|
+
analysis: TaskAnalysis
|
|
136
|
+
): Promise<DecompositionResult> {
|
|
137
|
+
const systemPrompt = `You are a task decomposition expert. Break down complex tasks into executable subtasks.
|
|
138
|
+
|
|
139
|
+
Rules:
|
|
140
|
+
1. Each subtask should be atomic and assignable to a single agent
|
|
141
|
+
2. Define clear dependencies between subtasks
|
|
142
|
+
3. Mark independent subtasks for parallel execution
|
|
143
|
+
4. Output valid JSON matching the WorkflowGraph schema
|
|
144
|
+
|
|
145
|
+
Available agents: IntentClassifierAgent, SpecAgent, ArchitectureAgent, TaskPlannerAgent, CodeGeneratorAgent, TestAgent, ValidatorAgent, FixAgent, OptimizerAgent, DocumentationAgent, BenchmarkAgent, BrainstormAgent`;
|
|
146
|
+
|
|
147
|
+
const userPrompt = `Decompose this ${intent} task:
|
|
148
|
+
"${prompt}"
|
|
149
|
+
|
|
150
|
+
Complexity: ${analysis.complexity}
|
|
151
|
+
Patterns detected: ${analysis.detectedPatterns.join(", ") || "none"}
|
|
152
|
+
Preferred strategy: ${strategy.name}
|
|
153
|
+
|
|
154
|
+
Output JSON:`;
|
|
155
|
+
|
|
156
|
+
try {
|
|
157
|
+
const response = await this.provider!.callStructured<WorkflowGraph>(
|
|
158
|
+
`${systemPrompt}\n\n${userPrompt}`,
|
|
159
|
+
{
|
|
160
|
+
type: "object",
|
|
161
|
+
properties: {
|
|
162
|
+
id: { type: "string" },
|
|
163
|
+
nodes: {
|
|
164
|
+
type: "array",
|
|
165
|
+
items: {
|
|
166
|
+
type: "object",
|
|
167
|
+
properties: {
|
|
168
|
+
id: { type: "string" },
|
|
169
|
+
agent: { type: "string" },
|
|
170
|
+
dependsOn: { type: "array", items: { type: "string" } },
|
|
171
|
+
requireApproval: { type: "boolean" }
|
|
172
|
+
},
|
|
173
|
+
required: ["id", "agent", "dependsOn"]
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
required: ["id", "nodes"]
|
|
178
|
+
},
|
|
179
|
+
{ temperature: 0.3, maxTokens: 2000 }
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
return {
|
|
183
|
+
workflow: response.output,
|
|
184
|
+
subworkflows: [],
|
|
185
|
+
strategy: strategy.name,
|
|
186
|
+
confidence: 0.85,
|
|
187
|
+
reasoning: "LLM-based decomposition applied"
|
|
188
|
+
};
|
|
189
|
+
} catch (error) {
|
|
190
|
+
console.warn("[TaskDecomposer] LLM decomposition failed, falling back to heuristics");
|
|
191
|
+
return this.heuristicDecompose(prompt, intent, strategy, analysis, {});
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
private heuristicDecompose(
|
|
196
|
+
prompt: string,
|
|
197
|
+
intent: Intent,
|
|
198
|
+
strategy: DecompositionStrategy,
|
|
199
|
+
analysis: TaskAnalysis,
|
|
200
|
+
options?: { maxSubtasks?: number; preferParallel?: boolean }
|
|
201
|
+
): Promise<DecompositionResult> {
|
|
202
|
+
const nodes: WorkflowNode[] = [];
|
|
203
|
+
const subworkflows: WorkflowGraph[] = [];
|
|
204
|
+
|
|
205
|
+
switch (intent) {
|
|
206
|
+
case "generate_code":
|
|
207
|
+
nodes.push(
|
|
208
|
+
{ id: "analyze", agent: "IntentClassifierAgent", dependsOn: [] },
|
|
209
|
+
{ id: "spec", agent: "SpecAgent", dependsOn: ["analyze"] },
|
|
210
|
+
{ id: "architecture", agent: "ArchitectureAgent", dependsOn: ["spec"] },
|
|
211
|
+
{ id: "implement", agent: "CodeGeneratorAgent", dependsOn: ["architecture"] },
|
|
212
|
+
{ id: "test", agent: "TestAgent", dependsOn: ["implement"] },
|
|
213
|
+
{ id: "validate", agent: "ValidatorAgent", dependsOn: ["test"] }
|
|
214
|
+
);
|
|
215
|
+
break;
|
|
216
|
+
|
|
217
|
+
case "fix":
|
|
218
|
+
nodes.push(
|
|
219
|
+
{ id: "diagnose", agent: "ValidatorAgent", dependsOn: [] },
|
|
220
|
+
{ id: "fix", agent: "FixAgent", dependsOn: ["diagnose"] },
|
|
221
|
+
{ id: "verify", agent: "ValidatorAgent", dependsOn: ["fix"] }
|
|
222
|
+
);
|
|
223
|
+
break;
|
|
224
|
+
|
|
225
|
+
case "test":
|
|
226
|
+
nodes.push(
|
|
227
|
+
{ id: "analyze", agent: "IntentClassifierAgent", dependsOn: [] },
|
|
228
|
+
{ id: "test_design", agent: "TestAgent", dependsOn: ["analyze"] },
|
|
229
|
+
{ id: "test_exec", agent: "ValidatorAgent", dependsOn: ["test_design"] }
|
|
230
|
+
);
|
|
231
|
+
break;
|
|
232
|
+
|
|
233
|
+
case "architecture":
|
|
234
|
+
nodes.push(
|
|
235
|
+
{ id: "requirements", agent: "SpecAgent", dependsOn: [] },
|
|
236
|
+
{ id: "design", agent: "ArchitectureAgent", dependsOn: ["requirements"] },
|
|
237
|
+
{ id: "plan", agent: "TaskPlannerAgent", dependsOn: ["design"] },
|
|
238
|
+
{ id: "review", agent: "ValidatorAgent", dependsOn: ["plan"] }
|
|
239
|
+
);
|
|
240
|
+
break;
|
|
241
|
+
|
|
242
|
+
case "document":
|
|
243
|
+
nodes.push(
|
|
244
|
+
{ id: "analyze", agent: "IntentClassifierAgent", dependsOn: [] },
|
|
245
|
+
{ id: "document", agent: "DocumentationAgent", dependsOn: ["analyze"] },
|
|
246
|
+
{ id: "validate", agent: "ValidatorAgent", dependsOn: ["document"] }
|
|
247
|
+
);
|
|
248
|
+
break;
|
|
249
|
+
|
|
250
|
+
case "optimize":
|
|
251
|
+
nodes.push(
|
|
252
|
+
{ id: "profile", agent: "BenchmarkAgent", dependsOn: [] },
|
|
253
|
+
{ id: "optimize", agent: "OptimizerAgent", dependsOn: ["profile"] },
|
|
254
|
+
{ id: "validate", agent: "ValidatorAgent", dependsOn: ["optimize"] },
|
|
255
|
+
{ id: "benchmark", agent: "BenchmarkAgent", dependsOn: ["validate"] }
|
|
256
|
+
);
|
|
257
|
+
break;
|
|
258
|
+
|
|
259
|
+
case "benchmark":
|
|
260
|
+
nodes.push(
|
|
261
|
+
{ id: "setup", agent: "BenchmarkAgent", dependsOn: [] },
|
|
262
|
+
{ id: "execute", agent: "BenchmarkAgent", dependsOn: ["setup"] },
|
|
263
|
+
{ id: "report", agent: "BenchmarkAgent", dependsOn: ["execute"] }
|
|
264
|
+
);
|
|
265
|
+
break;
|
|
266
|
+
|
|
267
|
+
default:
|
|
268
|
+
nodes.push(
|
|
269
|
+
{ id: "analyze", agent: "IntentClassifierAgent", dependsOn: [] },
|
|
270
|
+
{ id: "execute", agent: "BrainstormAgent", dependsOn: ["analyze"] }
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
if (analysis.complexity === "extreme" && nodes.length > 0) {
|
|
275
|
+
const lastNodeId = nodes[nodes.length - 1].id;
|
|
276
|
+
nodes.push({ id: "review", agent: "ValidatorAgent", dependsOn: [lastNodeId] });
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
if (options?.preferParallel && strategy.enableParallelization) {
|
|
280
|
+
const independentNodes = nodes.filter(n => n.dependsOn.length === 0);
|
|
281
|
+
if (independentNodes.length > 1) {
|
|
282
|
+
const parallelGateId = "parallel_gate";
|
|
283
|
+
for (const node of independentNodes) {
|
|
284
|
+
node.dependsOn = [parallelGateId];
|
|
285
|
+
}
|
|
286
|
+
nodes.unshift({ id: parallelGateId, agent: "IntentClassifierAgent", dependsOn: [] });
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
if (options?.maxSubtasks && nodes.length > options.maxSubtasks) {
|
|
291
|
+
const keptNodes = nodes.slice(0, options.maxSubtasks);
|
|
292
|
+
const lastKept = keptNodes[keptNodes.length - 1];
|
|
293
|
+
for (const node of keptNodes) {
|
|
294
|
+
node.dependsOn = node.dependsOn.filter(d => keptNodes.some(k => k.id === d));
|
|
295
|
+
}
|
|
296
|
+
nodes.length = 0;
|
|
297
|
+
nodes.push(...keptNodes);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
const workflow: WorkflowGraph = {
|
|
301
|
+
id: `wf_${intent}_${Date.now()}`,
|
|
302
|
+
nodes
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
return Promise.resolve({
|
|
306
|
+
workflow,
|
|
307
|
+
subworkflows,
|
|
308
|
+
strategy: strategy.name,
|
|
309
|
+
confidence: analysis.complexity === "low" ? 0.9 : 0.7,
|
|
310
|
+
reasoning: `Heuristic decomposition using ${strategy.name} strategy for ${analysis.complexity} complexity`
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
detectPatterns(prompt: string): Array<{ type: string; weight: number }> {
|
|
315
|
+
const detected: Array<{ type: string; weight: number }> = [];
|
|
316
|
+
for (const { pattern, weight, type } of COMPLEX_PATTERNS) {
|
|
317
|
+
if (pattern.test(prompt)) {
|
|
318
|
+
detected.push({ type, weight });
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
return detected;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
private calculateComplexity(
|
|
325
|
+
prompt: string,
|
|
326
|
+
patterns: Array<{ type: string; weight: number }>
|
|
327
|
+
): TaskAnalysis["complexity"] {
|
|
328
|
+
const wordCount = prompt.split(/\s+/).length;
|
|
329
|
+
const patternWeight = patterns.reduce((sum, p) => sum + p.weight, 0);
|
|
330
|
+
const conceptCount = patterns.length;
|
|
331
|
+
|
|
332
|
+
if (wordCount > COMPLEXITY_THRESHOLDS.high.maxWords || patternWeight >= 6 || conceptCount >= 5) {
|
|
333
|
+
return "extreme";
|
|
334
|
+
}
|
|
335
|
+
if (wordCount > COMPLEXITY_THRESHOLDS.medium.maxWords || patternWeight >= 3 || conceptCount >= 3) {
|
|
336
|
+
return "high";
|
|
337
|
+
}
|
|
338
|
+
if (wordCount > COMPLEXITY_THRESHOLDS.low.maxWords || patternWeight >= 1 || conceptCount >= 1) {
|
|
339
|
+
return "medium";
|
|
340
|
+
}
|
|
341
|
+
return "low";
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
private estimateTokens(prompt: string, complexity: TaskAnalysis["complexity"]): number {
|
|
345
|
+
const baseTokens = prompt.split(/\s+/).length * 1.3;
|
|
346
|
+
const multipliers = { low: 1, medium: 1.5, high: 2, extreme: 3 };
|
|
347
|
+
return Math.round(baseTokens * multipliers[complexity]);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
private selectStrategy(intent: Intent, complexity: TaskAnalysis["complexity"]): DecompositionStrategy {
|
|
351
|
+
const intentMatch = this.strategies.find(s => s.applicableIntents.includes(intent));
|
|
352
|
+
if (intentMatch) return intentMatch;
|
|
353
|
+
|
|
354
|
+
if (complexity === "extreme" || complexity === "high") {
|
|
355
|
+
return this.strategies.find(s => s.name === "parallel_verify") ?? this.strategies[0];
|
|
356
|
+
}
|
|
357
|
+
return this.strategies.find(s => s.name === "sequential") ?? this.strategies[0];
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
private identifyRisks(
|
|
361
|
+
prompt: string,
|
|
362
|
+
complexity: TaskAnalysis["complexity"],
|
|
363
|
+
patterns: Array<{ type: string; weight: number }>
|
|
364
|
+
): string[] {
|
|
365
|
+
const risks: string[] = [];
|
|
366
|
+
|
|
367
|
+
if (complexity === "extreme") {
|
|
368
|
+
risks.push("Task may exceed context window limits");
|
|
369
|
+
risks.push("Consider splitting into separate workflows");
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
if (patterns.some(p => p.type === "security")) {
|
|
373
|
+
risks.push("Security-sensitive task requires governance review");
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
if (patterns.some(p => p.type === "realtime")) {
|
|
377
|
+
risks.push("Real-time features may require specialized testing");
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
if (prompt.includes("urgent") || prompt.includes("asap") || prompt.includes("critical")) {
|
|
381
|
+
risks.push("Time-sensitive task flagged for priority execution");
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
return risks;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
async decomposeToSubtasks(
|
|
388
|
+
prompt: string,
|
|
389
|
+
options?: { constraints?: { maxSubtasks?: number; maxDuration?: number } }
|
|
390
|
+
): Promise<Subtask[]> {
|
|
391
|
+
return this.basicDecomposer.decompose(prompt, options);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
async generateSubWorkflows(
|
|
395
|
+
prompt: string,
|
|
396
|
+
intent: Intent,
|
|
397
|
+
analysis: TaskAnalysis
|
|
398
|
+
): Promise<WorkflowGraph[]> {
|
|
399
|
+
if (analysis.complexity !== "extreme") {
|
|
400
|
+
return [];
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
const patterns = this.detectPatterns(prompt);
|
|
404
|
+
const subworkflows: WorkflowGraph[] = [];
|
|
405
|
+
|
|
406
|
+
for (const pattern of patterns) {
|
|
407
|
+
const subWf: WorkflowGraph = {
|
|
408
|
+
id: `subwf_${pattern.type}_${Date.now()}`,
|
|
409
|
+
nodes: [
|
|
410
|
+
{ id: `${pattern.type}_spec`, agent: "SpecAgent", dependsOn: [] },
|
|
411
|
+
{ id: `${pattern.type}_impl`, agent: "CodeGeneratorAgent", dependsOn: [`${pattern.type}_spec`] },
|
|
412
|
+
{ id: `${pattern.type}_test`, agent: "TestAgent", dependsOn: [`${pattern.type}_impl`] }
|
|
413
|
+
]
|
|
414
|
+
};
|
|
415
|
+
subworkflows.push(subWf);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
return subworkflows;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
addStrategy(strategy: DecompositionStrategy): void {
|
|
422
|
+
this.strategies.push(strategy);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
getStrategies(): DecompositionStrategy[] {
|
|
426
|
+
return [...this.strategies];
|
|
427
|
+
}
|
|
428
|
+
}
|