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.
Files changed (48) hide show
  1. package/dist/governance/audit-trail.d.ts +94 -0
  2. package/dist/governance/audit-trail.d.ts.map +1 -0
  3. package/dist/governance/audit-trail.js +246 -0
  4. package/dist/governance/audit-trail.js.map +1 -0
  5. package/dist/governance/policy-engine.d.ts +101 -0
  6. package/dist/governance/policy-engine.d.ts.map +1 -0
  7. package/dist/governance/policy-engine.js +446 -0
  8. package/dist/governance/policy-engine.js.map +1 -0
  9. package/dist/governance/rbac-engine.d.ts +59 -0
  10. package/dist/governance/rbac-engine.d.ts.map +1 -0
  11. package/dist/governance/rbac-engine.js +183 -0
  12. package/dist/governance/rbac-engine.js.map +1 -0
  13. package/dist/index.d.ts +5 -1
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +5 -1
  16. package/dist/index.js.map +1 -1
  17. package/dist/memory/embeddings-service.d.ts +116 -0
  18. package/dist/memory/embeddings-service.d.ts.map +1 -0
  19. package/dist/memory/embeddings-service.js +287 -0
  20. package/dist/memory/embeddings-service.js.map +1 -0
  21. package/dist/memory/local-vector-store.d.ts +37 -3
  22. package/dist/memory/local-vector-store.d.ts.map +1 -1
  23. package/dist/memory/local-vector-store.js +91 -8
  24. package/dist/memory/local-vector-store.js.map +1 -1
  25. package/dist/orchestrator/core-orchestrator.d.ts +12 -0
  26. package/dist/orchestrator/core-orchestrator.d.ts.map +1 -1
  27. package/dist/orchestrator/core-orchestrator.js +75 -0
  28. package/dist/orchestrator/core-orchestrator.js.map +1 -1
  29. package/dist/orchestrator/task-decomposer.d.ts +56 -0
  30. package/dist/orchestrator/task-decomposer.d.ts.map +1 -0
  31. package/dist/orchestrator/task-decomposer.js +286 -0
  32. package/dist/orchestrator/task-decomposer.js.map +1 -0
  33. package/dist/plugins/plugin-system.d.ts +84 -1
  34. package/dist/plugins/plugin-system.d.ts.map +1 -1
  35. package/dist/plugins/plugin-system.js +91 -0
  36. package/dist/plugins/plugin-system.js.map +1 -1
  37. package/package.json +7 -7
  38. package/src/governance/audit-trail.ts +375 -0
  39. package/src/governance/policy-engine.ts +582 -0
  40. package/src/governance/rbac-engine.ts +244 -0
  41. package/src/index.ts +5 -2
  42. package/src/memory/embeddings-service.ts +322 -0
  43. package/src/memory/local-vector-store.ts +105 -8
  44. package/src/orchestrator/core-orchestrator.ts +78 -0
  45. package/src/orchestrator/task-decomposer.ts +428 -0
  46. package/src/plugins/plugin-system.ts +162 -1
  47. package/LICENSE +0 -21
  48. package/tsconfig.tsbuildinfo +0 -1
@@ -0,0 +1,244 @@
1
+ export type Role = "admin" | "user" | "viewer";
2
+ export type Permission =
3
+ | "workflow:read"
4
+ | "workflow:write"
5
+ | "workflow:execute"
6
+ | "workflow:delete"
7
+ | "config:read"
8
+ | "config:write"
9
+ | "metrics:read"
10
+ | "audit:read"
11
+ | "memory:read"
12
+ | "memory:write"
13
+ | "plugins:manage"
14
+ | "providers:configure";
15
+
16
+ export interface User {
17
+ id: string;
18
+ name: string;
19
+ email?: string;
20
+ role: Role;
21
+ permissions?: Permission[];
22
+ metadata?: Record<string, unknown>;
23
+ createdAt: number;
24
+ }
25
+
26
+ export interface RoleDefinition {
27
+ name: Role;
28
+ permissions: Permission[];
29
+ inherits?: Role;
30
+ description: string;
31
+ }
32
+
33
+ const DEFAULT_ROLES: RoleDefinition[] = [
34
+ {
35
+ name: "admin",
36
+ description: "Full system access",
37
+ permissions: [
38
+ "workflow:read", "workflow:write", "workflow:execute", "workflow:delete",
39
+ "config:read", "config:write",
40
+ "metrics:read", "audit:read",
41
+ "memory:read", "memory:write",
42
+ "plugins:manage", "providers:configure"
43
+ ]
44
+ },
45
+ {
46
+ name: "user",
47
+ description: "Standard user with execution capabilities",
48
+ permissions: [
49
+ "workflow:read", "workflow:write", "workflow:execute",
50
+ "config:read",
51
+ "metrics:read",
52
+ "memory:read", "memory:write"
53
+ ]
54
+ },
55
+ {
56
+ name: "viewer",
57
+ description: "Read-only access",
58
+ permissions: [
59
+ "workflow:read",
60
+ "metrics:read"
61
+ ]
62
+ }
63
+ ];
64
+
65
+ export class RBACEngine {
66
+ private roles: Map<Role, RoleDefinition> = new Map();
67
+ private users: Map<string, User> = new Map();
68
+ private customPermissions: Map<string, Permission[]> = new Map();
69
+
70
+ constructor(roles: RoleDefinition[] = DEFAULT_ROLES) {
71
+ for (const role of roles) {
72
+ this.roles.set(role.name, role);
73
+ }
74
+ }
75
+
76
+ createAdminUser(id: string, name: string, email?: string): User {
77
+ return this.createUser(id, name, "admin", email);
78
+ }
79
+
80
+ createUser(id: string, name: string, role: Role, email?: string): User {
81
+ const user: User = {
82
+ id,
83
+ name,
84
+ email,
85
+ role,
86
+ createdAt: Date.now()
87
+ };
88
+ this.users.set(id, user);
89
+ return user;
90
+ }
91
+
92
+ getUser(id: string): User | undefined {
93
+ return this.users.get(id);
94
+ }
95
+
96
+ updateUserRole(userId: string, role: Role): boolean {
97
+ const user = this.users.get(userId);
98
+ if (!user) return false;
99
+ user.role = role;
100
+ this.customPermissions.delete(userId);
101
+ return true;
102
+ }
103
+
104
+ grantPermission(userId: string, permission: Permission): boolean {
105
+ const user = this.users.get(userId);
106
+ if (!user) return false;
107
+
108
+ const existing = this.customPermissions.get(userId) || [];
109
+ if (!existing.includes(permission)) {
110
+ this.customPermissions.set(userId, [...existing, permission]);
111
+ }
112
+ return true;
113
+ }
114
+
115
+ revokePermission(userId: string, permission: Permission): boolean {
116
+ const user = this.users.get(userId);
117
+ if (!user) return false;
118
+
119
+ const existing = this.customPermissions.get(userId) || [];
120
+ const filtered = existing.filter(p => p !== permission);
121
+ this.customPermissions.set(userId, filtered);
122
+ return true;
123
+ }
124
+
125
+ hasPermission(userId: string, permission: Permission): boolean {
126
+ const user = this.users.get(userId);
127
+ if (!user) return false;
128
+
129
+ const customPerms = this.customPermissions.get(userId);
130
+ if (customPerms?.includes(permission)) return true;
131
+
132
+ const roleDef = this.roles.get(user.role);
133
+ if (!roleDef) return false;
134
+
135
+ if (roleDef.permissions.includes(permission)) return true;
136
+
137
+ if (roleDef.inherits) {
138
+ const parentRole = this.roles.get(roleDef.inherits);
139
+ return parentRole?.permissions.includes(permission) ?? false;
140
+ }
141
+
142
+ return false;
143
+ }
144
+
145
+ checkPermission(userId: string, permission: Permission): { allowed: boolean; reason?: string } {
146
+ if (this.hasPermission(userId, permission)) {
147
+ return { allowed: true };
148
+ }
149
+
150
+ const user = this.users.get(userId);
151
+ if (!user) {
152
+ return { allowed: false, reason: "User not found" };
153
+ }
154
+
155
+ return { allowed: false, reason: `Role '${user.role}' does not have permission '${permission}'` };
156
+ }
157
+
158
+ getUserPermissions(userId: string): Permission[] {
159
+ const user = this.users.get(userId);
160
+ if (!user) return [];
161
+
162
+ const roleDef = this.roles.get(user.role);
163
+ const rolePerms = roleDef?.permissions || [];
164
+ const customPerms = this.customPermissions.get(userId) || [];
165
+
166
+ return [...new Set([...rolePerms, ...customPerms])];
167
+ }
168
+
169
+ getRolePermissions(role: Role): Permission[] {
170
+ const roleDef = this.roles.get(role);
171
+ return roleDef?.permissions || [];
172
+ }
173
+
174
+ addRole(definition: RoleDefinition): void {
175
+ this.roles.set(definition.name, definition);
176
+ }
177
+
178
+ removeRole(role: Role): boolean {
179
+ if (["admin", "user", "viewer"].includes(role)) {
180
+ return false;
181
+ }
182
+ return this.roles.delete(role);
183
+ }
184
+
185
+ listRoles(): RoleDefinition[] {
186
+ return Array.from(this.roles.values());
187
+ }
188
+
189
+ listUsers(): User[] {
190
+ return Array.from(this.users.values());
191
+ }
192
+
193
+ deleteUser(userId: string): boolean {
194
+ this.customPermissions.delete(userId);
195
+ return this.users.delete(userId);
196
+ }
197
+
198
+ validateAccess(userId: string, resource: string, action: string): { allowed: boolean; reason?: string } {
199
+ const permission = `${resource}:${action}` as Permission;
200
+ return this.checkPermission(userId, permission);
201
+ }
202
+
203
+ canExecuteWorkflow(userId: string): boolean {
204
+ return this.hasPermission(userId, "workflow:execute");
205
+ }
206
+
207
+ canModifyConfig(userId: string): boolean {
208
+ return this.hasPermission(userId, "config:write");
209
+ }
210
+
211
+ canViewMetrics(userId: string): boolean {
212
+ return this.hasPermission(userId, "metrics:read");
213
+ }
214
+
215
+ canManagePlugins(userId: string): boolean {
216
+ return this.hasPermission(userId, "plugins:manage");
217
+ }
218
+
219
+ canConfigureProviders(userId: string): boolean {
220
+ return this.hasPermission(userId, "providers:configure");
221
+ }
222
+
223
+ exportState(): { users: User[]; customPermissions: Record<string, Permission[]> } {
224
+ return {
225
+ users: Array.from(this.users.values()),
226
+ customPermissions: Object.fromEntries(this.customPermissions)
227
+ };
228
+ }
229
+
230
+ importState(state: { users: User[]; customPermissions?: Record<string, Permission[]> }): void {
231
+ this.users.clear();
232
+ this.customPermissions.clear();
233
+
234
+ for (const user of state.users) {
235
+ this.users.set(user.id, user);
236
+ }
237
+
238
+ if (state.customPermissions) {
239
+ for (const [userId, perms] of Object.entries(state.customPermissions)) {
240
+ this.customPermissions.set(userId, perms);
241
+ }
242
+ }
243
+ }
244
+ }
package/src/index.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  export * from "./types/contracts.js";
2
2
  export * from "./orchestrator/core-orchestrator.js";
3
3
  export * from "./orchestrator/routing.js";
4
- export * from "./orchestrator/decomposition.js";
4
+ export { Decomposition, Subtask, TaskChunk, decomposeTask } from "./orchestrator/decomposition-engine.js";
5
+ export * from "./orchestrator/task-decomposer.js";
5
6
  export * from "./orchestrator/default-workflow.js";
6
7
  export * from "./orchestrator/verify-fix.js";
7
8
  export * from "./orchestrator/kernel-bridge.js";
@@ -20,6 +21,8 @@ export * from "./memory/local-vector-store.js";
20
21
  export * from "./memory/graph-memory.js";
21
22
  export * from "./memory/memory-manager.js";
22
23
  export * from "./plugins/long-term-memory-plugin.js";
23
-
24
24
  export * from "./plugins/governance-plugin.js";
25
25
  export * from "./governance/policies/pii-policy.js";
26
+ export * from "./governance/rbac-engine.js";
27
+ export * from "./governance/audit-trail.js";
28
+ export * from "./governance/policy-engine.js";
@@ -0,0 +1,322 @@
1
+ /**
2
+ * EmbeddingsService: Convert text/outputs to vector embeddings.
3
+ * Supports OpenAI, Cohere, or local models.
4
+ * Used to feed the LocalVectorStore with semantic vectors.
5
+ */
6
+
7
+ export interface EmbeddingProvider {
8
+ name: string;
9
+ embed(text: string): Promise<number[]>;
10
+ batchEmbed(texts: string[]): Promise<number[][]>;
11
+ getDimensions(): number;
12
+ }
13
+
14
+ /**
15
+ * OpenAI embeddings provider
16
+ */
17
+ export class OpenAIEmbeddingProvider implements EmbeddingProvider {
18
+ name = "openai";
19
+ private dimension = 1536;
20
+
21
+ constructor(private apiKey: string, private model: string = "text-embedding-3-small") {}
22
+
23
+ async embed(text: string): Promise<number[]> {
24
+ const results = await this.batchEmbed([text]);
25
+ return results[0];
26
+ }
27
+
28
+ async batchEmbed(texts: string[]): Promise<number[][]> {
29
+ try {
30
+ const response = await fetch("https://api.openai.com/v1/embeddings", {
31
+ method: "POST",
32
+ headers: {
33
+ "Content-Type": "application/json",
34
+ "Authorization": `Bearer ${this.apiKey}`
35
+ },
36
+ body: JSON.stringify({
37
+ model: this.model,
38
+ input: texts
39
+ })
40
+ });
41
+
42
+ if (!response.ok) {
43
+ throw new Error(`OpenAI API error: ${response.statusText}`);
44
+ }
45
+
46
+ const data = await response.json() as any;
47
+ return data.data.sort((a: any, b: any) => a.index - b.index).map((item: any) => item.embedding);
48
+ } catch (err) {
49
+ console.error("OpenAI embedding error:", err);
50
+ throw err;
51
+ }
52
+ }
53
+
54
+ getDimensions(): number {
55
+ return this.dimension;
56
+ }
57
+ }
58
+
59
+ /**
60
+ * Cohere embeddings provider
61
+ */
62
+ export class CohereEmbeddingProvider implements EmbeddingProvider {
63
+ name = "cohere";
64
+ private dimension = 4096;
65
+
66
+ constructor(private apiKey: string, private model: string = "embed-english-v3.0") {}
67
+
68
+ async embed(text: string): Promise<number[]> {
69
+ const results = await this.batchEmbed([text]);
70
+ return results[0];
71
+ }
72
+
73
+ async batchEmbed(texts: string[]): Promise<number[][]> {
74
+ try {
75
+ const response = await fetch("https://api.cohere.ai/v1/embed", {
76
+ method: "POST",
77
+ headers: {
78
+ "Content-Type": "application/json",
79
+ "Authorization": `Bearer ${this.apiKey}`
80
+ },
81
+ body: JSON.stringify({
82
+ texts,
83
+ model: this.model,
84
+ input_type: "search_document"
85
+ })
86
+ });
87
+
88
+ if (!response.ok) {
89
+ throw new Error(`Cohere API error: ${response.statusText}`);
90
+ }
91
+
92
+ const data = await response.json() as any;
93
+ return data.embeddings;
94
+ } catch (err) {
95
+ console.error("Cohere embedding error:", err);
96
+ throw err;
97
+ }
98
+ }
99
+
100
+ getDimensions(): number {
101
+ return this.dimension;
102
+ }
103
+ }
104
+
105
+ /**
106
+ * Mock local embeddings provider (for testing/offline)
107
+ * Generates pseudo-random vectors based on text hash
108
+ */
109
+ export class LocalEmbeddingProvider implements EmbeddingProvider {
110
+ name = "local";
111
+ private dimension = 384;
112
+
113
+ constructor() {}
114
+
115
+ async embed(text: string): Promise<number[]> {
116
+ return this.textToVector(text);
117
+ }
118
+
119
+ async batchEmbed(texts: string[]): Promise<number[][]> {
120
+ return texts.map(text => this.textToVector(text));
121
+ }
122
+
123
+ getDimensions(): number {
124
+ return this.dimension;
125
+ }
126
+
127
+ private textToVector(text: string): number[] {
128
+ // Simple hash-based deterministic vector generation
129
+ const hash = this.simpleHash(text);
130
+ const vector: number[] = [];
131
+ let seed = hash;
132
+
133
+ for (let i = 0; i < this.dimension; i++) {
134
+ seed = (seed * 9301 + 49297) % 233280;
135
+ vector.push((seed / 233280) * 2 - 1); // Normalize to [-1, 1]
136
+ }
137
+
138
+ // Normalize to unit vector
139
+ const magnitude = Math.sqrt(vector.reduce((sum, v) => sum + v * v, 0));
140
+ return vector.map(v => magnitude > 0 ? v / magnitude : 0);
141
+ }
142
+
143
+ private simpleHash(str: string): number {
144
+ let hash = 0;
145
+ for (let i = 0; i < str.length; i++) {
146
+ const char = str.charCodeAt(i);
147
+ hash = ((hash << 5) - hash) + char;
148
+ hash = hash & hash; // Convert to 32bit integer
149
+ }
150
+ return Math.abs(hash);
151
+ }
152
+ }
153
+
154
+ /**
155
+ * Embeddings Service: Orchestrates embedding generation and caching
156
+ */
157
+ export class EmbeddingsService {
158
+ private provider: EmbeddingProvider;
159
+ private cache: Map<string, number[]> = new Map();
160
+ private cacheHits = 0;
161
+ private cacheMisses = 0;
162
+
163
+ constructor(provider: EmbeddingProvider | string = "local") {
164
+ if (typeof provider === "string") {
165
+ this.provider = this.getDefaultProvider(provider);
166
+ } else {
167
+ this.provider = provider;
168
+ }
169
+ }
170
+
171
+ /**
172
+ * Get a default provider by name
173
+ */
174
+ private getDefaultProvider(name: string): EmbeddingProvider {
175
+ const apiKey = process.env[`${name.toUpperCase()}_API_KEY`] || "";
176
+
177
+ switch (name.toLowerCase()) {
178
+ case "openai":
179
+ return new OpenAIEmbeddingProvider(apiKey);
180
+ case "cohere":
181
+ return new CohereEmbeddingProvider(apiKey);
182
+ case "local":
183
+ default:
184
+ return new LocalEmbeddingProvider();
185
+ }
186
+ }
187
+
188
+ /**
189
+ * Embed a single text, with caching
190
+ */
191
+ async embed(text: string, useCache = true): Promise<number[]> {
192
+ const cacheKey = this.getCacheKey(text);
193
+
194
+ if (useCache && this.cache.has(cacheKey)) {
195
+ this.cacheHits++;
196
+ return this.cache.get(cacheKey)!;
197
+ }
198
+
199
+ this.cacheMisses++;
200
+ const vector = await this.provider.embed(text);
201
+ this.cache.set(cacheKey, vector);
202
+ return vector;
203
+ }
204
+
205
+ /**
206
+ * Embed multiple texts
207
+ */
208
+ async batchEmbed(texts: string[], useCache = true): Promise<number[][]> {
209
+ const results: number[][] = [];
210
+ const missingIndices: number[] = [];
211
+ const missingTexts: string[] = [];
212
+
213
+ // Check cache first
214
+ for (let i = 0; i < texts.length; i++) {
215
+ const cacheKey = this.getCacheKey(texts[i]);
216
+ if (useCache && this.cache.has(cacheKey)) {
217
+ results[i] = this.cache.get(cacheKey)!;
218
+ this.cacheHits++;
219
+ } else {
220
+ missingIndices.push(i);
221
+ missingTexts.push(texts[i]);
222
+ }
223
+ }
224
+
225
+ // Embed missing texts
226
+ if (missingTexts.length > 0) {
227
+ this.cacheMisses += missingTexts.length;
228
+ const vectors = await this.provider.batchEmbed(missingTexts);
229
+
230
+ for (let i = 0; i < missingTexts.length; i++) {
231
+ const cacheKey = this.getCacheKey(missingTexts[i]);
232
+ results[missingIndices[i]] = vectors[i];
233
+ this.cache.set(cacheKey, vectors[i]);
234
+ }
235
+ }
236
+
237
+ return results;
238
+ }
239
+
240
+ /**
241
+ * Clear cache and reset stats
242
+ */
243
+ clearCache(): void {
244
+ this.cache.clear();
245
+ this.cacheHits = 0;
246
+ this.cacheMisses = 0;
247
+ }
248
+
249
+ /**
250
+ * Get cache statistics
251
+ */
252
+ getCacheStats() {
253
+ const total = this.cacheHits + this.cacheMisses;
254
+ return {
255
+ cacheHits: this.cacheHits,
256
+ cacheMisses: this.cacheMisses,
257
+ hitRate: total > 0 ? (this.cacheHits / total * 100).toFixed(2) + "%" : "N/A",
258
+ cacheSize: this.cache.size,
259
+ provider: this.provider.name,
260
+ dimensions: this.provider.getDimensions()
261
+ };
262
+ }
263
+
264
+ /**
265
+ * Set embeddings provider
266
+ */
267
+ setProvider(provider: EmbeddingProvider): void {
268
+ this.provider = provider;
269
+ this.clearCache();
270
+ }
271
+
272
+ /**
273
+ * Get current provider
274
+ */
275
+ getProvider(): EmbeddingProvider {
276
+ return this.provider;
277
+ }
278
+
279
+ /**
280
+ * Get dimensions of embedding vectors
281
+ */
282
+ getDimensions(): number {
283
+ return this.provider.getDimensions();
284
+ }
285
+
286
+ /**
287
+ * Cache key generation (simple MD5 alternative)
288
+ */
289
+ private getCacheKey(text: string): string {
290
+ // Simple deterministic hash for caching
291
+ return Buffer.from(text).toString("base64");
292
+ }
293
+
294
+ /**
295
+ * Pre-warm cache with common prompts/outputs
296
+ */
297
+ async warmCache(documents: string[]): Promise<void> {
298
+ await this.batchEmbed(documents, false);
299
+ }
300
+ }
301
+
302
+ /**
303
+ * Create embeddings service from environment or config
304
+ */
305
+ export function createEmbeddingsService(config?: {
306
+ provider?: "openai" | "cohere" | "local";
307
+ apiKey?: string;
308
+ }): EmbeddingsService {
309
+ const providerName = config?.provider || process.env.EMBEDDINGS_PROVIDER || "local";
310
+
311
+ if (providerName === "openai") {
312
+ const apiKey = config?.apiKey || process.env.OPENAI_API_KEY;
313
+ if (!apiKey) throw new Error("OPENAI_API_KEY not set");
314
+ return new EmbeddingsService(new OpenAIEmbeddingProvider(apiKey));
315
+ } else if (providerName === "cohere") {
316
+ const apiKey = config?.apiKey || process.env.COHERE_API_KEY;
317
+ if (!apiKey) throw new Error("COHERE_API_KEY not set");
318
+ return new EmbeddingsService(new CohereEmbeddingProvider(apiKey));
319
+ } else {
320
+ return new EmbeddingsService(new LocalEmbeddingProvider());
321
+ }
322
+ }