forbocai 0.2.0 → 0.3.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/README.md CHANGED
@@ -78,6 +78,31 @@ npm install forbocai
78
78
 
79
79
  ---
80
80
 
81
+ ## CLI Reference
82
+
83
+ `CLI_Tóols // Cómmand_Líne`
84
+
85
+ The SDK includes a comprehensive CLI for managing your AI infrastructure.
86
+
87
+ ```bash
88
+ # Agents
89
+ npx forbocai agent create "A wise wizard"
90
+ npx forbocai agent list
91
+ npx forbocai agent chat <agentId>
92
+
93
+ # Memory & Bridge
94
+ npx forbocai memory recall <agentId> "battle"
95
+ npx forbocai bridge validate action.json
96
+
97
+ # System
98
+ npx forbocai status
99
+ npx forbocai doctor
100
+ ```
101
+
102
+ See [full CLI documentation](https://forbocai.docs.buildwithfern.com/docs/pages/cli).
103
+
104
+ ---
105
+
81
106
  ## Quick Start
82
107
 
83
108
  `Quíck_Stárt // Éxample`
package/dist/index.d.mts CHANGED
@@ -17,7 +17,7 @@ interface CortexStatus {
17
17
  id: string;
18
18
  model: string;
19
19
  ready: boolean;
20
- engine: 'webballm' | 'mock' | 'remote';
20
+ engine: 'webllm' | 'mock' | 'remote';
21
21
  }
22
22
  interface CompletionOptions {
23
23
  temperature?: number;
@@ -107,7 +107,7 @@ interface ICortex {
107
107
  */
108
108
  declare const createCortex: (config: CortexConfig) => ICortex;
109
109
  /**
110
- * Pure function to generate completion text
110
+ * Pure function to generate completion text (Fallback/Mock)
111
111
  * Deterministic output for same input (functional principle)
112
112
  */
113
113
  declare const generateCompletion: (prompt: string, options: CompletionOptions, modelId: string) => string;
@@ -173,14 +173,14 @@ interface IMemory {
173
173
  recall(query: string, limit?: number): Promise<MemoryItem[]>;
174
174
  list(limit?: number, offset?: number): Promise<MemoryItem[]>;
175
175
  clear(): Promise<void>;
176
- export(): MemoryItem[];
176
+ export(): Promise<MemoryItem[]>;
177
177
  import(memories: MemoryItem[]): Promise<void>;
178
178
  }
179
179
  /**
180
180
  * Configuration for the memory module
181
181
  */
182
182
  interface MemoryModuleConfig extends MemoryConfig {
183
- /** Storage key for IndexedDB persistence */
183
+ /** Storage key/Database name for IndexedDB persistence */
184
184
  storageKey?: string;
185
185
  /** API URL for optional cloud sync */
186
186
  apiUrl?: string;
@@ -210,7 +210,7 @@ declare const rankMemoriesByRelevance: (memories: readonly MemoryItem[], query:
210
210
  */
211
211
  declare const createMemory: (config?: MemoryModuleConfig) => IMemory;
212
212
  /**
213
- * Mock Memory for testing
213
+ * Mock Memory for testing (In-Memory Fallback)
214
214
  */
215
215
  declare class MockMemory implements IMemory {
216
216
  private memories;
@@ -218,7 +218,7 @@ declare class MockMemory implements IMemory {
218
218
  recall(query: string, limit?: number): Promise<MemoryItem[]>;
219
219
  list(limit?: number, offset?: number): Promise<MemoryItem[]>;
220
220
  clear(): Promise<void>;
221
- export(): MemoryItem[];
221
+ export(): Promise<MemoryItem[]>;
222
222
  import(memories: MemoryItem[]): Promise<void>;
223
223
  }
224
224
 
@@ -313,6 +313,8 @@ interface SoulExportConfig {
313
313
  includeMemories?: boolean;
314
314
  /** Sign with wallet (for NFT minting) */
315
315
  sign?: boolean;
316
+ /** Use local IPFS node (Helia) */
317
+ useLocalNode?: boolean;
316
318
  }
317
319
  /**
318
320
  * Soul export result
@@ -331,6 +333,8 @@ interface SoulImportConfig {
331
333
  apiUrl?: string;
332
334
  /** Verify signature before import */
333
335
  verifySignature?: boolean;
336
+ /** Use local IPFS node (Helia) */
337
+ useLocalNode?: boolean;
334
338
  }
335
339
  /**
336
340
  * Interface for Soul operations
@@ -359,11 +363,11 @@ declare const validateSoul: (soul: Soul) => {
359
363
  errors: string[];
360
364
  };
361
365
  /**
362
- * Export agent Soul to IPFS via API
366
+ * Export agent Soul to IPFS (Helia or API)
363
367
  */
364
368
  declare const exportSoulToIPFS: (agentId: string, soul: Soul, config?: SoulExportConfig) => Promise<SoulExportResult>;
365
369
  /**
366
- * Import Soul from IPFS by CID
370
+ * Import Soul from IPFS by CID (Helia or API)
367
371
  */
368
372
  declare const importSoulFromIPFS: (cid: string, config?: SoulImportConfig) => Promise<Soul>;
369
373
  /**
package/dist/index.d.ts CHANGED
@@ -17,7 +17,7 @@ interface CortexStatus {
17
17
  id: string;
18
18
  model: string;
19
19
  ready: boolean;
20
- engine: 'webballm' | 'mock' | 'remote';
20
+ engine: 'webllm' | 'mock' | 'remote';
21
21
  }
22
22
  interface CompletionOptions {
23
23
  temperature?: number;
@@ -107,7 +107,7 @@ interface ICortex {
107
107
  */
108
108
  declare const createCortex: (config: CortexConfig) => ICortex;
109
109
  /**
110
- * Pure function to generate completion text
110
+ * Pure function to generate completion text (Fallback/Mock)
111
111
  * Deterministic output for same input (functional principle)
112
112
  */
113
113
  declare const generateCompletion: (prompt: string, options: CompletionOptions, modelId: string) => string;
@@ -173,14 +173,14 @@ interface IMemory {
173
173
  recall(query: string, limit?: number): Promise<MemoryItem[]>;
174
174
  list(limit?: number, offset?: number): Promise<MemoryItem[]>;
175
175
  clear(): Promise<void>;
176
- export(): MemoryItem[];
176
+ export(): Promise<MemoryItem[]>;
177
177
  import(memories: MemoryItem[]): Promise<void>;
178
178
  }
179
179
  /**
180
180
  * Configuration for the memory module
181
181
  */
182
182
  interface MemoryModuleConfig extends MemoryConfig {
183
- /** Storage key for IndexedDB persistence */
183
+ /** Storage key/Database name for IndexedDB persistence */
184
184
  storageKey?: string;
185
185
  /** API URL for optional cloud sync */
186
186
  apiUrl?: string;
@@ -210,7 +210,7 @@ declare const rankMemoriesByRelevance: (memories: readonly MemoryItem[], query:
210
210
  */
211
211
  declare const createMemory: (config?: MemoryModuleConfig) => IMemory;
212
212
  /**
213
- * Mock Memory for testing
213
+ * Mock Memory for testing (In-Memory Fallback)
214
214
  */
215
215
  declare class MockMemory implements IMemory {
216
216
  private memories;
@@ -218,7 +218,7 @@ declare class MockMemory implements IMemory {
218
218
  recall(query: string, limit?: number): Promise<MemoryItem[]>;
219
219
  list(limit?: number, offset?: number): Promise<MemoryItem[]>;
220
220
  clear(): Promise<void>;
221
- export(): MemoryItem[];
221
+ export(): Promise<MemoryItem[]>;
222
222
  import(memories: MemoryItem[]): Promise<void>;
223
223
  }
224
224
 
@@ -313,6 +313,8 @@ interface SoulExportConfig {
313
313
  includeMemories?: boolean;
314
314
  /** Sign with wallet (for NFT minting) */
315
315
  sign?: boolean;
316
+ /** Use local IPFS node (Helia) */
317
+ useLocalNode?: boolean;
316
318
  }
317
319
  /**
318
320
  * Soul export result
@@ -331,6 +333,8 @@ interface SoulImportConfig {
331
333
  apiUrl?: string;
332
334
  /** Verify signature before import */
333
335
  verifySignature?: boolean;
336
+ /** Use local IPFS node (Helia) */
337
+ useLocalNode?: boolean;
334
338
  }
335
339
  /**
336
340
  * Interface for Soul operations
@@ -359,11 +363,11 @@ declare const validateSoul: (soul: Soul) => {
359
363
  errors: string[];
360
364
  };
361
365
  /**
362
- * Export agent Soul to IPFS via API
366
+ * Export agent Soul to IPFS (Helia or API)
363
367
  */
364
368
  declare const exportSoulToIPFS: (agentId: string, soul: Soul, config?: SoulExportConfig) => Promise<SoulExportResult>;
365
369
  /**
366
- * Import Soul from IPFS by CID
370
+ * Import Soul from IPFS by CID (Helia or API)
367
371
  */
368
372
  declare const importSoulFromIPFS: (cid: string, config?: SoulImportConfig) => Promise<Soul>;
369
373
  /**
package/dist/index.js CHANGED
@@ -65,14 +65,18 @@ __export(index_exports, {
65
65
  module.exports = __toCommonJS(index_exports);
66
66
 
67
67
  // src/cortex.ts
68
+ var import_web_llm = require("@mlc-ai/web-llm");
69
+ var MODEL_CONFIG = {
70
+ "smollm2-135m": "HuggingFaceTB/SmolLM2-135M-Instruct-q4f16_1-MLC",
71
+ "llama3-8b": "Llama-3-8B-Instruct-q4f16_1-MLC",
72
+ "default": "HuggingFaceTB/SmolLM2-135M-Instruct-q4f16_1-MLC"
73
+ };
68
74
  var createCortex = (config) => {
69
75
  return new WebLLMCortex(config);
70
76
  };
71
77
  var generateCompletion = (prompt, options, modelId) => {
72
- const baseResponse = `Response to: ${prompt}`;
73
78
  const temperature = options.temperature ?? 0.7;
74
- const maxTokens = options.maxTokens ?? 1024;
75
- return baseResponse;
79
+ return `[Mock Response] Model: ${modelId}, Temp: ${temperature}. Response to: ${prompt}`;
76
80
  };
77
81
  var processObservationToDirective = (observation) => {
78
82
  return {
@@ -90,6 +94,7 @@ var generateActionFromDirective = (directive) => {
90
94
  var WebLLMCortex = class {
91
95
  constructor(config) {
92
96
  this.status = null;
97
+ this.engine = null;
93
98
  this.config = {
94
99
  ...config,
95
100
  temperature: config.temperature ?? 0.7,
@@ -101,53 +106,70 @@ var WebLLMCortex = class {
101
106
  if (this.status?.ready) {
102
107
  return this.status;
103
108
  }
104
- const apiUrl = this.config.apiUrl || "http://localhost:8080";
105
109
  try {
106
- const response = await fetch(`${apiUrl}/cortex/init`, {
107
- method: "POST",
108
- headers: { "Content-Type": "application/json" },
109
- body: JSON.stringify({
110
- requestedModel: this.config.model,
111
- authKey: this.config.authKey
112
- // Send the key!
113
- })
114
- });
115
- if (!response.ok) {
116
- console.warn("Cortex Authorization Failed:", response.statusText);
117
- throw new Error("Authorization Failed");
110
+ const isBrowser = typeof window !== "undefined";
111
+ if (isBrowser && this.config.gpu) {
112
+ console.log(`> Initializing WebLLM with model: ${this.config.model}`);
113
+ const selectedModel = MODEL_CONFIG[this.config.model] || MODEL_CONFIG["default"];
114
+ this.engine = await (0, import_web_llm.CreateMLCEngine)(selectedModel, {
115
+ initProgressCallback: (progress) => {
116
+ console.log(`> Model Loading: ${(progress.progress * 100).toFixed(1)}% - ${progress.text}`);
117
+ }
118
+ });
119
+ this.status = {
120
+ id: "webllm-" + Math.random().toString(36).substring(7),
121
+ model: this.config.model,
122
+ ready: true,
123
+ engine: "webllm"
124
+ };
125
+ return this.status;
126
+ } else {
127
+ console.warn("WebGPU unavailable (Node.js or disabled). Falling back to mock/API.");
128
+ throw new Error("WebGPU unavailable");
118
129
  }
119
- const data = await response.json();
120
- const newStatus = {
121
- id: data.cortexId,
122
- model: this.config.model,
123
- ready: true,
124
- engine: "webballm"
125
- };
126
- this.status = newStatus;
127
- return newStatus;
128
130
  } catch (e) {
129
- console.warn("Failed to connect to Neural Grid. Falling back to offline mode.");
130
- const newStatus = {
131
+ console.warn("Using offline/mock mode due to initialization failure:", e);
132
+ this.status = {
131
133
  id: "offline-" + Math.random().toString(36).substring(7),
132
134
  model: this.config.model,
133
135
  ready: true,
134
136
  engine: "mock"
135
137
  };
136
- this.status = newStatus;
137
- return newStatus;
138
+ return this.status;
138
139
  }
139
140
  }
140
141
  async complete(prompt, options = {}) {
141
- if (!this.status?.ready) {
142
- throw new Error("Cortex not initialized");
142
+ if (!this.status?.ready) await this.init();
143
+ if (this.engine) {
144
+ const response = await this.engine.chat.completions.create({
145
+ messages: [{ role: "user", content: prompt }],
146
+ temperature: options.temperature ?? this.config.temperature,
147
+ max_tokens: options.maxTokens ?? this.config.maxTokens
148
+ });
149
+ return response.choices[0]?.message?.content || "";
150
+ } else {
151
+ return generateCompletion(prompt, options, this.config.model);
143
152
  }
144
- const result = generateCompletion(prompt, options, this.config.model);
145
- return result;
146
153
  }
147
154
  async *completeStream(prompt, options = {}) {
148
- const result = await this.complete(prompt, options);
149
- for (const char of result) {
150
- yield char;
155
+ if (!this.status?.ready) await this.init();
156
+ if (this.engine) {
157
+ const stream = await this.engine.chat.completions.create({
158
+ messages: [{ role: "user", content: prompt }],
159
+ temperature: options.temperature ?? this.config.temperature,
160
+ max_tokens: options.maxTokens ?? this.config.maxTokens,
161
+ stream: true
162
+ });
163
+ for await (const chunk of stream) {
164
+ const content = chunk.choices[0]?.delta?.content || "";
165
+ if (content) yield content;
166
+ }
167
+ } else {
168
+ const result = generateCompletion(prompt, options, this.config.model);
169
+ for (const char of result) {
170
+ await new Promise((r) => setTimeout(r, 10));
171
+ yield char;
172
+ }
151
173
  }
152
174
  }
153
175
  async processObservation(observation) {
@@ -167,21 +189,24 @@ var MockCortex = class {
167
189
  };
168
190
  }
169
191
  async complete(prompt) {
170
- return generateCompletion(prompt, {}, "mock-model");
192
+ return `Mock response to: ${prompt}`;
171
193
  }
172
194
  async *completeStream(prompt) {
173
- yield `Mock streaming response to: ${prompt}`;
195
+ yield `Mock `;
196
+ yield `streaming `;
197
+ yield `response `;
198
+ yield `to: ${prompt}`;
174
199
  }
175
200
  async processObservation(observation) {
176
201
  return {
177
202
  type: "system-prompt",
178
- content: "Mock processing of observation"
203
+ content: "Mock processing"
179
204
  };
180
205
  }
181
206
  async generateAction(directive) {
182
207
  return {
183
208
  type: "mock-action",
184
- reason: "Mock action generation"
209
+ reason: "Mock action"
185
210
  };
186
211
  }
187
212
  };
@@ -300,6 +325,7 @@ var fromSoul = (soul, cortex) => {
300
325
  };
301
326
 
302
327
  // src/memory.ts
328
+ var import_idb = require("idb");
303
329
  var createMemoryItem = (text, type = "observation", importance = 0.5) => {
304
330
  return {
305
331
  id: `mem_${Date.now()}_${Math.random().toString(36).substring(7)}`,
@@ -337,129 +363,128 @@ var rankMemoriesByRelevance = (memories, query, limit = 5) => {
337
363
  return scored.sort((a, b) => b.score - a.score).slice(0, limit).map((s) => s.memory);
338
364
  };
339
365
  var createMemory = (config = {}) => {
340
- return new LocalMemory(config);
366
+ return new IndexedDBMemory(config);
341
367
  };
342
- var LocalMemory = class {
368
+ var IndexedDBMemory = class {
343
369
  constructor(config) {
370
+ this.DB_NAME = "forbocai_db";
371
+ this.STORE_NAME = "memories";
344
372
  this.config = {
345
373
  decay: config.decay ?? "none",
346
374
  maxContextWindow: config.maxContextWindow ?? 10,
347
375
  storageKey: config.storageKey ?? "forbocai_memory",
348
376
  ...config
349
377
  };
350
- this.memories = [];
351
- this.loadFromStorage();
378
+ if (typeof window !== "undefined" && window.indexedDB) {
379
+ this.dbPromise = (0, import_idb.openDB)(this.config.storageKey || this.DB_NAME, 1, {
380
+ upgrade(db) {
381
+ if (!db.objectStoreNames.contains("memories")) {
382
+ const store = db.createObjectStore("memories", { keyPath: "id" });
383
+ store.createIndex("timestamp", "timestamp");
384
+ store.createIndex("agentId", "agentId");
385
+ }
386
+ }
387
+ });
388
+ } else {
389
+ console.warn("IndexedDB not available (Node.js environment). Falling back to in-memory.");
390
+ this.dbPromise = Promise.resolve({
391
+ getAll: async () => [],
392
+ put: async () => {
393
+ },
394
+ clear: async () => {
395
+ },
396
+ getAllFromIndex: async () => [],
397
+ transaction: () => ({ objectStore: () => ({ put: async () => {
398
+ } }), done: Promise.resolve() })
399
+ });
400
+ }
401
+ }
402
+ async getAllMemories() {
403
+ const db = await this.dbPromise;
404
+ try {
405
+ return await db.getAll(this.STORE_NAME) || [];
406
+ } catch {
407
+ return [];
408
+ }
352
409
  }
353
- /**
354
- * Store a new memory observation
355
- */
356
410
  async store(text, type = "observation", importance = 0.5) {
357
411
  const memory = createMemoryItem(text, type, importance);
358
- this.memories = [...this.memories, memory];
359
- this.saveToStorage();
412
+ const db = await this.dbPromise;
413
+ try {
414
+ await db.put(this.STORE_NAME, memory);
415
+ } catch (e) {
416
+ }
360
417
  if (this.config.apiUrl && this.config.agentId) {
361
- try {
362
- await fetch(`${this.config.apiUrl}/agents/${this.config.agentId}/memory`, {
363
- method: "POST",
364
- headers: { "Content-Type": "application/json" },
365
- body: JSON.stringify({
366
- observation: text,
367
- importance
368
- })
369
- });
370
- } catch (e) {
371
- console.warn("Failed to sync memory to API:", e);
372
- }
418
+ this.syncToApi(text, importance).catch(() => {
419
+ });
373
420
  }
374
421
  return memory;
375
422
  }
376
- /**
377
- * Recall memories relevant to a query (semantic search)
378
- */
423
+ async syncToApi(text, importance) {
424
+ try {
425
+ await fetch(`${this.config.apiUrl}/agents/${this.config.agentId}/memory`, {
426
+ method: "POST",
427
+ headers: { "Content-Type": "application/json" },
428
+ body: JSON.stringify({ observation: text, importance })
429
+ });
430
+ } catch (e) {
431
+ }
432
+ }
379
433
  async recall(query, limit = 5) {
380
- let processedMemories = this.memories;
434
+ let memories = await this.getAllMemories();
381
435
  if (this.config.decay === "temporal") {
382
436
  const now = Date.now();
383
- processedMemories = this.memories.map(
384
- (mem) => applyTemporalDecay(mem, now)
385
- );
437
+ memories = memories.map((mem) => applyTemporalDecay(mem, now));
386
438
  }
387
- const results = rankMemoriesByRelevance(
388
- processedMemories,
389
- query,
390
- limit
391
- );
439
+ const results = rankMemoriesByRelevance(memories, query, limit);
392
440
  if (this.config.apiUrl && this.config.agentId) {
393
441
  try {
394
- const response = await fetch(
395
- `${this.config.apiUrl}/agents/${this.config.agentId}/memory/recall`,
396
- {
397
- method: "POST",
398
- headers: { "Content-Type": "application/json" },
399
- body: JSON.stringify({ query, similarity: 0.5 })
400
- }
401
- );
402
- if (response.ok) {
403
- const apiMemories = await response.json();
404
- return [...results, ...apiMemories.slice(0, limit - results.length)];
405
- }
442
+ const apiResults = await this.recallFromApi(query);
443
+ return [...results, ...apiResults.slice(0, limit - results.length)];
406
444
  } catch (e) {
407
- console.warn("Failed to recall from API:", e);
408
445
  }
409
446
  }
410
447
  return results;
411
448
  }
412
- /**
413
- * List all memories (paginated)
414
- */
449
+ async recallFromApi(query) {
450
+ const response = await fetch(
451
+ `${this.config.apiUrl}/agents/${this.config.agentId}/memory/recall`,
452
+ {
453
+ method: "POST",
454
+ headers: { "Content-Type": "application/json" },
455
+ body: JSON.stringify({ query, similarity: 0.5 })
456
+ }
457
+ );
458
+ if (response.ok) return await response.json();
459
+ return [];
460
+ }
415
461
  async list(limit = 50, offset = 0) {
416
- return this.memories.slice(offset, offset + limit).map((m) => ({ ...m }));
462
+ const db = await this.dbPromise;
463
+ try {
464
+ const all = await db.getAllFromIndex(this.STORE_NAME, "timestamp");
465
+ return all.reverse().slice(offset, offset + limit);
466
+ } catch {
467
+ return [];
468
+ }
417
469
  }
418
- /**
419
- * Clear all memories
420
- */
421
470
  async clear() {
422
- this.memories = [];
423
- this.saveToStorage();
424
- }
425
- /**
426
- * Export all memories for backup/portability
427
- */
428
- export() {
429
- return this.memories.map((m) => ({ ...m }));
430
- }
431
- /**
432
- * Import memories (e.g., from Soul)
433
- */
434
- async import(memories) {
435
- const existingIds = new Set(this.memories.map((m) => m.id));
436
- const newMemories = memories.filter((m) => !existingIds.has(m.id));
437
- this.memories = [...this.memories, ...newMemories];
438
- this.saveToStorage();
439
- }
440
- /**
441
- * Load memories from localStorage (browser) or skip (Node)
442
- */
443
- loadFromStorage() {
444
- if (typeof window === "undefined" || !window.localStorage) return;
471
+ const db = await this.dbPromise;
445
472
  try {
446
- const stored = localStorage.getItem(this.config.storageKey);
447
- if (stored) {
448
- this.memories = JSON.parse(stored);
449
- }
450
- } catch (e) {
451
- console.warn("Failed to load memories from storage:", e);
473
+ await db.clear(this.STORE_NAME);
474
+ } catch {
452
475
  }
453
476
  }
454
- /**
455
- * Save memories to localStorage (browser) or skip (Node)
456
- */
457
- saveToStorage() {
458
- if (typeof window === "undefined" || !window.localStorage) return;
477
+ async export() {
478
+ return this.getAllMemories();
479
+ }
480
+ async import(memories) {
481
+ const db = await this.dbPromise;
459
482
  try {
460
- localStorage.setItem(this.config.storageKey, JSON.stringify(this.memories));
461
- } catch (e) {
462
- console.warn("Failed to save memories to storage:", e);
483
+ const tx = db.transaction(this.STORE_NAME, "readwrite");
484
+ const store = tx.objectStore(this.STORE_NAME);
485
+ for (const mem of memories) await store.put(mem);
486
+ await tx.done;
487
+ } catch {
463
488
  }
464
489
  }
465
490
  };
@@ -481,7 +506,7 @@ var MockMemory = class {
481
506
  async clear() {
482
507
  this.memories = [];
483
508
  }
484
- export() {
509
+ async export() {
485
510
  return [...this.memories];
486
511
  }
487
512
  async import(memories) {
@@ -780,6 +805,24 @@ var validateAction = (action, rules, context = {}) => {
780
805
  };
781
806
 
782
807
  // src/soul.ts
808
+ var import_helia = require("helia");
809
+ var import_json = require("@helia/json");
810
+ var import_blockstore_core = require("blockstore-core");
811
+ var heliaNode = null;
812
+ var heliaJson = null;
813
+ var getHelia = async () => {
814
+ if (heliaNode) return { node: heliaNode, j: heliaJson };
815
+ try {
816
+ const blockstore = new import_blockstore_core.MemoryBlockstore();
817
+ heliaNode = await (0, import_helia.createHelia)({ blockstore });
818
+ heliaJson = (0, import_json.json)(heliaNode);
819
+ console.log("> Helia IPFS Node Initialized:", heliaNode.libp2p.peerId.toString());
820
+ return { node: heliaNode, j: heliaJson };
821
+ } catch (e) {
822
+ console.warn("Failed to init Helia (IPFS):", e);
823
+ return null;
824
+ }
825
+ };
783
826
  var createSoul = (id, name, persona, state, memories = []) => {
784
827
  return {
785
828
  id,
@@ -793,8 +836,8 @@ var createSoul = (id, name, persona, state, memories = []) => {
793
836
  var serializeSoul = (soul) => {
794
837
  return JSON.stringify(soul, null, 2);
795
838
  };
796
- var deserializeSoul = (json) => {
797
- const parsed = JSON.parse(json);
839
+ var deserializeSoul = (json2) => {
840
+ const parsed = JSON.parse(json2);
798
841
  if (!parsed.id || !parsed.persona || !parsed.state) {
799
842
  throw new Error("Invalid Soul format: missing required fields");
800
843
  }
@@ -820,6 +863,21 @@ var validateSoul = (soul) => {
820
863
  };
821
864
  };
822
865
  var exportSoulToIPFS = async (agentId, soul, config = {}) => {
866
+ if (config.useLocalNode !== false) {
867
+ const helia = await getHelia();
868
+ if (helia) {
869
+ try {
870
+ const cid = await helia.j.add(soul);
871
+ return {
872
+ cid: cid.toString(),
873
+ ipfsUrl: `ipfs://${cid.toString()}`,
874
+ soul
875
+ };
876
+ } catch (e) {
877
+ console.warn("Helia add failed, falling back to API", e);
878
+ }
879
+ }
880
+ }
823
881
  const apiUrl = config.apiUrl || "https://forbocai-api.onrender.com";
824
882
  try {
825
883
  const response = await fetch(`${apiUrl}/agents/${agentId}/soul/export`, {
@@ -847,6 +905,14 @@ var exportSoulToIPFS = async (agentId, soul, config = {}) => {
847
905
  }
848
906
  };
849
907
  var importSoulFromIPFS = async (cid, config = {}) => {
908
+ if (config.useLocalNode !== false) {
909
+ const helia = await getHelia();
910
+ if (helia) {
911
+ try {
912
+ } catch (e) {
913
+ }
914
+ }
915
+ }
850
916
  const apiUrl = config.apiUrl || "https://forbocai-api.onrender.com";
851
917
  try {
852
918
  const response = await fetch(`${apiUrl}/souls/${cid}`, {
@@ -862,7 +928,6 @@ var importSoulFromIPFS = async (cid, config = {}) => {
862
928
  version: "1.0.0",
863
929
  name: data.soulName,
864
930
  persona: data.dna,
865
- // DNA contains the persona
866
931
  state: { mood: "neutral", inventory: [], skills: {}, relationships: {} },
867
932
  memories: []
868
933
  };
package/dist/index.mjs CHANGED
@@ -1,12 +1,16 @@
1
1
  // src/cortex.ts
2
+ import { CreateMLCEngine } from "@mlc-ai/web-llm";
3
+ var MODEL_CONFIG = {
4
+ "smollm2-135m": "HuggingFaceTB/SmolLM2-135M-Instruct-q4f16_1-MLC",
5
+ "llama3-8b": "Llama-3-8B-Instruct-q4f16_1-MLC",
6
+ "default": "HuggingFaceTB/SmolLM2-135M-Instruct-q4f16_1-MLC"
7
+ };
2
8
  var createCortex = (config) => {
3
9
  return new WebLLMCortex(config);
4
10
  };
5
11
  var generateCompletion = (prompt, options, modelId) => {
6
- const baseResponse = `Response to: ${prompt}`;
7
12
  const temperature = options.temperature ?? 0.7;
8
- const maxTokens = options.maxTokens ?? 1024;
9
- return baseResponse;
13
+ return `[Mock Response] Model: ${modelId}, Temp: ${temperature}. Response to: ${prompt}`;
10
14
  };
11
15
  var processObservationToDirective = (observation) => {
12
16
  return {
@@ -24,6 +28,7 @@ var generateActionFromDirective = (directive) => {
24
28
  var WebLLMCortex = class {
25
29
  constructor(config) {
26
30
  this.status = null;
31
+ this.engine = null;
27
32
  this.config = {
28
33
  ...config,
29
34
  temperature: config.temperature ?? 0.7,
@@ -35,53 +40,70 @@ var WebLLMCortex = class {
35
40
  if (this.status?.ready) {
36
41
  return this.status;
37
42
  }
38
- const apiUrl = this.config.apiUrl || "http://localhost:8080";
39
43
  try {
40
- const response = await fetch(`${apiUrl}/cortex/init`, {
41
- method: "POST",
42
- headers: { "Content-Type": "application/json" },
43
- body: JSON.stringify({
44
- requestedModel: this.config.model,
45
- authKey: this.config.authKey
46
- // Send the key!
47
- })
48
- });
49
- if (!response.ok) {
50
- console.warn("Cortex Authorization Failed:", response.statusText);
51
- throw new Error("Authorization Failed");
44
+ const isBrowser = typeof window !== "undefined";
45
+ if (isBrowser && this.config.gpu) {
46
+ console.log(`> Initializing WebLLM with model: ${this.config.model}`);
47
+ const selectedModel = MODEL_CONFIG[this.config.model] || MODEL_CONFIG["default"];
48
+ this.engine = await CreateMLCEngine(selectedModel, {
49
+ initProgressCallback: (progress) => {
50
+ console.log(`> Model Loading: ${(progress.progress * 100).toFixed(1)}% - ${progress.text}`);
51
+ }
52
+ });
53
+ this.status = {
54
+ id: "webllm-" + Math.random().toString(36).substring(7),
55
+ model: this.config.model,
56
+ ready: true,
57
+ engine: "webllm"
58
+ };
59
+ return this.status;
60
+ } else {
61
+ console.warn("WebGPU unavailable (Node.js or disabled). Falling back to mock/API.");
62
+ throw new Error("WebGPU unavailable");
52
63
  }
53
- const data = await response.json();
54
- const newStatus = {
55
- id: data.cortexId,
56
- model: this.config.model,
57
- ready: true,
58
- engine: "webballm"
59
- };
60
- this.status = newStatus;
61
- return newStatus;
62
64
  } catch (e) {
63
- console.warn("Failed to connect to Neural Grid. Falling back to offline mode.");
64
- const newStatus = {
65
+ console.warn("Using offline/mock mode due to initialization failure:", e);
66
+ this.status = {
65
67
  id: "offline-" + Math.random().toString(36).substring(7),
66
68
  model: this.config.model,
67
69
  ready: true,
68
70
  engine: "mock"
69
71
  };
70
- this.status = newStatus;
71
- return newStatus;
72
+ return this.status;
72
73
  }
73
74
  }
74
75
  async complete(prompt, options = {}) {
75
- if (!this.status?.ready) {
76
- throw new Error("Cortex not initialized");
76
+ if (!this.status?.ready) await this.init();
77
+ if (this.engine) {
78
+ const response = await this.engine.chat.completions.create({
79
+ messages: [{ role: "user", content: prompt }],
80
+ temperature: options.temperature ?? this.config.temperature,
81
+ max_tokens: options.maxTokens ?? this.config.maxTokens
82
+ });
83
+ return response.choices[0]?.message?.content || "";
84
+ } else {
85
+ return generateCompletion(prompt, options, this.config.model);
77
86
  }
78
- const result = generateCompletion(prompt, options, this.config.model);
79
- return result;
80
87
  }
81
88
  async *completeStream(prompt, options = {}) {
82
- const result = await this.complete(prompt, options);
83
- for (const char of result) {
84
- yield char;
89
+ if (!this.status?.ready) await this.init();
90
+ if (this.engine) {
91
+ const stream = await this.engine.chat.completions.create({
92
+ messages: [{ role: "user", content: prompt }],
93
+ temperature: options.temperature ?? this.config.temperature,
94
+ max_tokens: options.maxTokens ?? this.config.maxTokens,
95
+ stream: true
96
+ });
97
+ for await (const chunk of stream) {
98
+ const content = chunk.choices[0]?.delta?.content || "";
99
+ if (content) yield content;
100
+ }
101
+ } else {
102
+ const result = generateCompletion(prompt, options, this.config.model);
103
+ for (const char of result) {
104
+ await new Promise((r) => setTimeout(r, 10));
105
+ yield char;
106
+ }
85
107
  }
86
108
  }
87
109
  async processObservation(observation) {
@@ -101,21 +123,24 @@ var MockCortex = class {
101
123
  };
102
124
  }
103
125
  async complete(prompt) {
104
- return generateCompletion(prompt, {}, "mock-model");
126
+ return `Mock response to: ${prompt}`;
105
127
  }
106
128
  async *completeStream(prompt) {
107
- yield `Mock streaming response to: ${prompt}`;
129
+ yield `Mock `;
130
+ yield `streaming `;
131
+ yield `response `;
132
+ yield `to: ${prompt}`;
108
133
  }
109
134
  async processObservation(observation) {
110
135
  return {
111
136
  type: "system-prompt",
112
- content: "Mock processing of observation"
137
+ content: "Mock processing"
113
138
  };
114
139
  }
115
140
  async generateAction(directive) {
116
141
  return {
117
142
  type: "mock-action",
118
- reason: "Mock action generation"
143
+ reason: "Mock action"
119
144
  };
120
145
  }
121
146
  };
@@ -234,6 +259,7 @@ var fromSoul = (soul, cortex) => {
234
259
  };
235
260
 
236
261
  // src/memory.ts
262
+ import { openDB } from "idb";
237
263
  var createMemoryItem = (text, type = "observation", importance = 0.5) => {
238
264
  return {
239
265
  id: `mem_${Date.now()}_${Math.random().toString(36).substring(7)}`,
@@ -271,129 +297,128 @@ var rankMemoriesByRelevance = (memories, query, limit = 5) => {
271
297
  return scored.sort((a, b) => b.score - a.score).slice(0, limit).map((s) => s.memory);
272
298
  };
273
299
  var createMemory = (config = {}) => {
274
- return new LocalMemory(config);
300
+ return new IndexedDBMemory(config);
275
301
  };
276
- var LocalMemory = class {
302
+ var IndexedDBMemory = class {
277
303
  constructor(config) {
304
+ this.DB_NAME = "forbocai_db";
305
+ this.STORE_NAME = "memories";
278
306
  this.config = {
279
307
  decay: config.decay ?? "none",
280
308
  maxContextWindow: config.maxContextWindow ?? 10,
281
309
  storageKey: config.storageKey ?? "forbocai_memory",
282
310
  ...config
283
311
  };
284
- this.memories = [];
285
- this.loadFromStorage();
312
+ if (typeof window !== "undefined" && window.indexedDB) {
313
+ this.dbPromise = openDB(this.config.storageKey || this.DB_NAME, 1, {
314
+ upgrade(db) {
315
+ if (!db.objectStoreNames.contains("memories")) {
316
+ const store = db.createObjectStore("memories", { keyPath: "id" });
317
+ store.createIndex("timestamp", "timestamp");
318
+ store.createIndex("agentId", "agentId");
319
+ }
320
+ }
321
+ });
322
+ } else {
323
+ console.warn("IndexedDB not available (Node.js environment). Falling back to in-memory.");
324
+ this.dbPromise = Promise.resolve({
325
+ getAll: async () => [],
326
+ put: async () => {
327
+ },
328
+ clear: async () => {
329
+ },
330
+ getAllFromIndex: async () => [],
331
+ transaction: () => ({ objectStore: () => ({ put: async () => {
332
+ } }), done: Promise.resolve() })
333
+ });
334
+ }
335
+ }
336
+ async getAllMemories() {
337
+ const db = await this.dbPromise;
338
+ try {
339
+ return await db.getAll(this.STORE_NAME) || [];
340
+ } catch {
341
+ return [];
342
+ }
286
343
  }
287
- /**
288
- * Store a new memory observation
289
- */
290
344
  async store(text, type = "observation", importance = 0.5) {
291
345
  const memory = createMemoryItem(text, type, importance);
292
- this.memories = [...this.memories, memory];
293
- this.saveToStorage();
346
+ const db = await this.dbPromise;
347
+ try {
348
+ await db.put(this.STORE_NAME, memory);
349
+ } catch (e) {
350
+ }
294
351
  if (this.config.apiUrl && this.config.agentId) {
295
- try {
296
- await fetch(`${this.config.apiUrl}/agents/${this.config.agentId}/memory`, {
297
- method: "POST",
298
- headers: { "Content-Type": "application/json" },
299
- body: JSON.stringify({
300
- observation: text,
301
- importance
302
- })
303
- });
304
- } catch (e) {
305
- console.warn("Failed to sync memory to API:", e);
306
- }
352
+ this.syncToApi(text, importance).catch(() => {
353
+ });
307
354
  }
308
355
  return memory;
309
356
  }
310
- /**
311
- * Recall memories relevant to a query (semantic search)
312
- */
357
+ async syncToApi(text, importance) {
358
+ try {
359
+ await fetch(`${this.config.apiUrl}/agents/${this.config.agentId}/memory`, {
360
+ method: "POST",
361
+ headers: { "Content-Type": "application/json" },
362
+ body: JSON.stringify({ observation: text, importance })
363
+ });
364
+ } catch (e) {
365
+ }
366
+ }
313
367
  async recall(query, limit = 5) {
314
- let processedMemories = this.memories;
368
+ let memories = await this.getAllMemories();
315
369
  if (this.config.decay === "temporal") {
316
370
  const now = Date.now();
317
- processedMemories = this.memories.map(
318
- (mem) => applyTemporalDecay(mem, now)
319
- );
371
+ memories = memories.map((mem) => applyTemporalDecay(mem, now));
320
372
  }
321
- const results = rankMemoriesByRelevance(
322
- processedMemories,
323
- query,
324
- limit
325
- );
373
+ const results = rankMemoriesByRelevance(memories, query, limit);
326
374
  if (this.config.apiUrl && this.config.agentId) {
327
375
  try {
328
- const response = await fetch(
329
- `${this.config.apiUrl}/agents/${this.config.agentId}/memory/recall`,
330
- {
331
- method: "POST",
332
- headers: { "Content-Type": "application/json" },
333
- body: JSON.stringify({ query, similarity: 0.5 })
334
- }
335
- );
336
- if (response.ok) {
337
- const apiMemories = await response.json();
338
- return [...results, ...apiMemories.slice(0, limit - results.length)];
339
- }
376
+ const apiResults = await this.recallFromApi(query);
377
+ return [...results, ...apiResults.slice(0, limit - results.length)];
340
378
  } catch (e) {
341
- console.warn("Failed to recall from API:", e);
342
379
  }
343
380
  }
344
381
  return results;
345
382
  }
346
- /**
347
- * List all memories (paginated)
348
- */
383
+ async recallFromApi(query) {
384
+ const response = await fetch(
385
+ `${this.config.apiUrl}/agents/${this.config.agentId}/memory/recall`,
386
+ {
387
+ method: "POST",
388
+ headers: { "Content-Type": "application/json" },
389
+ body: JSON.stringify({ query, similarity: 0.5 })
390
+ }
391
+ );
392
+ if (response.ok) return await response.json();
393
+ return [];
394
+ }
349
395
  async list(limit = 50, offset = 0) {
350
- return this.memories.slice(offset, offset + limit).map((m) => ({ ...m }));
396
+ const db = await this.dbPromise;
397
+ try {
398
+ const all = await db.getAllFromIndex(this.STORE_NAME, "timestamp");
399
+ return all.reverse().slice(offset, offset + limit);
400
+ } catch {
401
+ return [];
402
+ }
351
403
  }
352
- /**
353
- * Clear all memories
354
- */
355
404
  async clear() {
356
- this.memories = [];
357
- this.saveToStorage();
358
- }
359
- /**
360
- * Export all memories for backup/portability
361
- */
362
- export() {
363
- return this.memories.map((m) => ({ ...m }));
364
- }
365
- /**
366
- * Import memories (e.g., from Soul)
367
- */
368
- async import(memories) {
369
- const existingIds = new Set(this.memories.map((m) => m.id));
370
- const newMemories = memories.filter((m) => !existingIds.has(m.id));
371
- this.memories = [...this.memories, ...newMemories];
372
- this.saveToStorage();
373
- }
374
- /**
375
- * Load memories from localStorage (browser) or skip (Node)
376
- */
377
- loadFromStorage() {
378
- if (typeof window === "undefined" || !window.localStorage) return;
405
+ const db = await this.dbPromise;
379
406
  try {
380
- const stored = localStorage.getItem(this.config.storageKey);
381
- if (stored) {
382
- this.memories = JSON.parse(stored);
383
- }
384
- } catch (e) {
385
- console.warn("Failed to load memories from storage:", e);
407
+ await db.clear(this.STORE_NAME);
408
+ } catch {
386
409
  }
387
410
  }
388
- /**
389
- * Save memories to localStorage (browser) or skip (Node)
390
- */
391
- saveToStorage() {
392
- if (typeof window === "undefined" || !window.localStorage) return;
411
+ async export() {
412
+ return this.getAllMemories();
413
+ }
414
+ async import(memories) {
415
+ const db = await this.dbPromise;
393
416
  try {
394
- localStorage.setItem(this.config.storageKey, JSON.stringify(this.memories));
395
- } catch (e) {
396
- console.warn("Failed to save memories to storage:", e);
417
+ const tx = db.transaction(this.STORE_NAME, "readwrite");
418
+ const store = tx.objectStore(this.STORE_NAME);
419
+ for (const mem of memories) await store.put(mem);
420
+ await tx.done;
421
+ } catch {
397
422
  }
398
423
  }
399
424
  };
@@ -415,7 +440,7 @@ var MockMemory = class {
415
440
  async clear() {
416
441
  this.memories = [];
417
442
  }
418
- export() {
443
+ async export() {
419
444
  return [...this.memories];
420
445
  }
421
446
  async import(memories) {
@@ -714,6 +739,24 @@ var validateAction = (action, rules, context = {}) => {
714
739
  };
715
740
 
716
741
  // src/soul.ts
742
+ import { createHelia } from "helia";
743
+ import { json } from "@helia/json";
744
+ import { MemoryBlockstore } from "blockstore-core";
745
+ var heliaNode = null;
746
+ var heliaJson = null;
747
+ var getHelia = async () => {
748
+ if (heliaNode) return { node: heliaNode, j: heliaJson };
749
+ try {
750
+ const blockstore = new MemoryBlockstore();
751
+ heliaNode = await createHelia({ blockstore });
752
+ heliaJson = json(heliaNode);
753
+ console.log("> Helia IPFS Node Initialized:", heliaNode.libp2p.peerId.toString());
754
+ return { node: heliaNode, j: heliaJson };
755
+ } catch (e) {
756
+ console.warn("Failed to init Helia (IPFS):", e);
757
+ return null;
758
+ }
759
+ };
717
760
  var createSoul = (id, name, persona, state, memories = []) => {
718
761
  return {
719
762
  id,
@@ -727,8 +770,8 @@ var createSoul = (id, name, persona, state, memories = []) => {
727
770
  var serializeSoul = (soul) => {
728
771
  return JSON.stringify(soul, null, 2);
729
772
  };
730
- var deserializeSoul = (json) => {
731
- const parsed = JSON.parse(json);
773
+ var deserializeSoul = (json2) => {
774
+ const parsed = JSON.parse(json2);
732
775
  if (!parsed.id || !parsed.persona || !parsed.state) {
733
776
  throw new Error("Invalid Soul format: missing required fields");
734
777
  }
@@ -754,6 +797,21 @@ var validateSoul = (soul) => {
754
797
  };
755
798
  };
756
799
  var exportSoulToIPFS = async (agentId, soul, config = {}) => {
800
+ if (config.useLocalNode !== false) {
801
+ const helia = await getHelia();
802
+ if (helia) {
803
+ try {
804
+ const cid = await helia.j.add(soul);
805
+ return {
806
+ cid: cid.toString(),
807
+ ipfsUrl: `ipfs://${cid.toString()}`,
808
+ soul
809
+ };
810
+ } catch (e) {
811
+ console.warn("Helia add failed, falling back to API", e);
812
+ }
813
+ }
814
+ }
757
815
  const apiUrl = config.apiUrl || "https://forbocai-api.onrender.com";
758
816
  try {
759
817
  const response = await fetch(`${apiUrl}/agents/${agentId}/soul/export`, {
@@ -781,6 +839,14 @@ var exportSoulToIPFS = async (agentId, soul, config = {}) => {
781
839
  }
782
840
  };
783
841
  var importSoulFromIPFS = async (cid, config = {}) => {
842
+ if (config.useLocalNode !== false) {
843
+ const helia = await getHelia();
844
+ if (helia) {
845
+ try {
846
+ } catch (e) {
847
+ }
848
+ }
849
+ }
784
850
  const apiUrl = config.apiUrl || "https://forbocai-api.onrender.com";
785
851
  try {
786
852
  const response = await fetch(`${apiUrl}/souls/${cid}`, {
@@ -796,7 +862,6 @@ var importSoulFromIPFS = async (cid, config = {}) => {
796
862
  version: "1.0.0",
797
863
  name: data.soulName,
798
864
  persona: data.dna,
799
- // DNA contains the persona
800
865
  state: { mood: "neutral", inventory: [], skills: {}, relationships: {} },
801
866
  memories: []
802
867
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "forbocai",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "The Infrastructure Layer for Autonomous AI Characters",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -16,15 +16,20 @@
16
16
  "postinstall": "node postinstall.js"
17
17
  },
18
18
  "dependencies": {
19
- "@mlc-ai/web-llm": "^0.2.0",
20
- "zod": "^3.22.4",
21
- "axios": "^1.6.2"
19
+ "@helia/json": "^5.0.6",
20
+ "@mlc-ai/web-llm": "^0.2.80",
21
+ "axios": "^1.6.2",
22
+ "blockstore-core": "^6.1.2",
23
+ "datastore-core": "^11.0.2",
24
+ "helia": "^6.0.18",
25
+ "idb": "^8.0.3",
26
+ "zod": "^3.22.4"
22
27
  },
23
28
  "devDependencies": {
29
+ "@types/node": "^20.0.0",
24
30
  "tsup": "^8.5.1",
25
31
  "typescript": "^5.9.3",
26
- "vitest": "^1.0.0",
27
- "@types/node": "^20.0.0"
32
+ "vitest": "^1.0.0"
28
33
  },
29
34
  "files": [
30
35
  "dist",