forbocai 0.2.0 → 0.3.2

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/index.mjs CHANGED
@@ -1,124 +1,179 @@
1
1
  // src/cortex.ts
2
- var createCortex = (config) => {
3
- return new WebLLMCortex(config);
2
+ import * as fs from "fs";
3
+ import * as path from "path";
4
+ import * as https from "https";
5
+ var MODEL_URLS = {
6
+ "smollm2-135m": "https://huggingface.co/HuggingFaceTB/SmolLM2-135M-Instruct-GGUF/resolve/main/smollm2-135m-instruct-q4_k_m.gguf",
7
+ "llama3-8b": "https://huggingface.co/lmstudio-community/Meta-Llama-3-8B-Instruct-GGUF/resolve/main/Meta-Llama-3-8B-Instruct-Q4_K_M.gguf"
4
8
  };
5
- var generateCompletion = (prompt, options, modelId) => {
6
- const baseResponse = `Response to: ${prompt}`;
7
- const temperature = options.temperature ?? 0.7;
8
- const maxTokens = options.maxTokens ?? 1024;
9
- return baseResponse;
9
+ var DEFAULT_MODEL = "smollm2-135m";
10
+ var ensureDirectoryExists = (dirPath) => {
11
+ if (!fs.existsSync(dirPath)) {
12
+ fs.mkdirSync(dirPath, { recursive: true });
13
+ }
10
14
  };
11
- var processObservationToDirective = (observation) => {
12
- return {
13
- type: "system-prompt",
14
- content: "You are an autonomous agent in a game world. Process the observation and generate appropriate actions.",
15
- priority: "normal"
16
- };
15
+ var downloadFile = (url, destPath) => {
16
+ return new Promise((resolve, reject) => {
17
+ const file = fs.createWriteStream(destPath);
18
+ https.get(url, (response) => {
19
+ if (response.statusCode === 302 || response.statusCode === 301) {
20
+ downloadFile(response.headers.location, destPath).then(resolve).catch(reject);
21
+ return;
22
+ }
23
+ if (response.statusCode !== 200) {
24
+ reject(new Error(`Failed to download: ${response.statusCode}`));
25
+ return;
26
+ }
27
+ response.pipe(file);
28
+ file.on("finish", () => {
29
+ file.close();
30
+ resolve();
31
+ });
32
+ }).on("error", (err) => {
33
+ fs.unlink(destPath, () => {
34
+ });
35
+ reject(err);
36
+ });
37
+ });
17
38
  };
18
- var generateActionFromDirective = (directive) => {
19
- return {
20
- type: "idle",
21
- reason: directive.content ?? "No specific directive provided"
39
+ var createNativeCortex = (config) => {
40
+ let status = {
41
+ id: "native-init",
42
+ model: config.model || DEFAULT_MODEL,
43
+ ready: false,
44
+ engine: "node-llama-cpp"
22
45
  };
23
- };
24
- var WebLLMCortex = class {
25
- constructor(config) {
26
- this.status = null;
27
- this.config = {
28
- ...config,
29
- temperature: config.temperature ?? 0.7,
30
- maxTokens: config.maxTokens ?? 1024,
31
- gpu: config.gpu ?? true
32
- };
33
- }
34
- async init() {
35
- if (this.status?.ready) {
36
- return this.status;
37
- }
38
- const apiUrl = this.config.apiUrl || "http://localhost:8080";
46
+ let llama;
47
+ let model;
48
+ let context;
49
+ let session;
50
+ const MODELS_DIR = path.join(process.cwd(), "local_infrastructure", "models");
51
+ const init2 = async () => {
52
+ if (status.ready) return status;
39
53
  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");
54
+ console.log("> Initializing Native Cortex (node-llama-cpp)...");
55
+ ensureDirectoryExists(MODELS_DIR);
56
+ const modelKey = config.model || DEFAULT_MODEL;
57
+ const modelUrl = MODEL_URLS[modelKey] || MODEL_URLS[DEFAULT_MODEL];
58
+ const modelFileName = path.basename(modelUrl);
59
+ const modelPath = path.join(MODELS_DIR, modelFileName);
60
+ if (!fs.existsSync(modelPath)) {
61
+ console.log(`> Downloading Model: ${modelKey}...`);
62
+ console.log(`> Source: ${modelUrl}`);
63
+ await downloadFile(modelUrl, modelPath);
64
+ console.log(`> Download complete.`);
65
+ } else {
66
+ console.log(`> Using cached model: ${modelPath}`);
52
67
  }
53
- const data = await response.json();
54
- const newStatus = {
55
- id: data.cortexId,
56
- model: this.config.model,
68
+ const { getLlama, LlamaChatSession } = await import("node-llama-cpp");
69
+ llama = await getLlama();
70
+ console.log("> Loading model into memory...");
71
+ model = await llama.loadModel({
72
+ modelPath,
73
+ gpuLayers: config.gpu ? 99 : 0
74
+ // Try to use GPU if allowed
75
+ });
76
+ console.log("> Creating context...");
77
+ context = await model.createContext();
78
+ session = new LlamaChatSession({
79
+ contextSequence: context.getSequence()
80
+ });
81
+ status = {
82
+ id: `ctx_${Date.now()}`,
83
+ model: modelKey,
57
84
  ready: true,
58
- engine: "webballm"
85
+ engine: "node-llama-cpp"
59
86
  };
60
- this.status = newStatus;
61
- return newStatus;
87
+ console.log("> Cortex Ready.");
88
+ return status;
62
89
  } catch (e) {
63
- console.warn("Failed to connect to Neural Grid. Falling back to offline mode.");
64
- const newStatus = {
65
- id: "offline-" + Math.random().toString(36).substring(7),
66
- model: this.config.model,
67
- ready: true,
68
- engine: "mock"
69
- };
70
- this.status = newStatus;
71
- return newStatus;
90
+ console.error("Failed to initialize Native Cortex:", e);
91
+ throw e;
72
92
  }
73
- }
74
- async complete(prompt, options = {}) {
75
- if (!this.status?.ready) {
76
- throw new Error("Cortex not initialized");
93
+ };
94
+ const complete = async (prompt, options = {}) => {
95
+ if (!status.ready) await init2();
96
+ return await session.prompt(prompt, {
97
+ maxTokens: options.maxTokens,
98
+ temperature: options.temperature
99
+ });
100
+ };
101
+ const completeStream = async function* (prompt, options = {}) {
102
+ if (!status.ready) await init2();
103
+ const tokenQueue = [];
104
+ let resolveNext = null;
105
+ let isComplete = false;
106
+ const generationPromise = session.prompt(prompt, {
107
+ maxTokens: options.maxTokens,
108
+ temperature: options.temperature,
109
+ onToken: (tokens) => {
110
+ try {
111
+ const text = model.detokenize(tokens);
112
+ if (text) {
113
+ tokenQueue.push(text);
114
+ if (resolveNext) {
115
+ resolveNext();
116
+ resolveNext = null;
117
+ }
118
+ }
119
+ } catch {
120
+ tokenQueue.push(tokens.map((t) => String.fromCharCode(t % 256)).join(""));
121
+ }
122
+ }
123
+ }).then(() => {
124
+ isComplete = true;
125
+ if (resolveNext) resolveNext();
126
+ });
127
+ while (!isComplete || tokenQueue.length > 0) {
128
+ if (tokenQueue.length > 0) {
129
+ yield tokenQueue.shift();
130
+ } else if (!isComplete) {
131
+ await new Promise((resolve) => {
132
+ resolveNext = resolve;
133
+ });
134
+ }
77
135
  }
78
- const result = generateCompletion(prompt, options, this.config.model);
79
- return result;
80
- }
81
- async *completeStream(prompt, options = {}) {
82
- const result = await this.complete(prompt, options);
83
- for (const char of result) {
84
- yield char;
136
+ await generationPromise;
137
+ };
138
+ const embed = async (text) => {
139
+ if (!status.ready) await init2();
140
+ try {
141
+ return new Array(384).fill(0).map(() => Math.random());
142
+ } catch (e) {
143
+ return [];
85
144
  }
86
- }
87
- async processObservation(observation) {
88
- return processObservationToDirective(observation);
89
- }
90
- async generateAction(directive) {
91
- return generateActionFromDirective(directive);
92
- }
93
- };
94
- var MockCortex = class {
95
- async init() {
96
- return {
97
- id: "mock-cortex",
98
- model: "mock-model",
99
- ready: true,
100
- engine: "mock"
101
- };
102
- }
103
- async complete(prompt) {
104
- return generateCompletion(prompt, {}, "mock-model");
105
- }
106
- async *completeStream(prompt) {
107
- yield `Mock streaming response to: ${prompt}`;
108
- }
109
- async processObservation(observation) {
110
- return {
111
- type: "system-prompt",
112
- content: "Mock processing of observation"
113
- };
114
- }
115
- async generateAction(directive) {
116
- return {
117
- type: "mock-action",
118
- reason: "Mock action generation"
119
- };
120
- }
145
+ };
146
+ const processObservation = async (obs) => {
147
+ const prompt = `System: You are an agent.
148
+ Observation: ${obs.content}
149
+ Task: Generete a JSON directive { "type": "...", "content": "..." }.`;
150
+ const res = await complete(prompt);
151
+ try {
152
+ return JSON.parse(res);
153
+ } catch {
154
+ return { type: "thought", content: res };
155
+ }
156
+ };
157
+ const generateAction = async (dir) => {
158
+ const prompt = `Directive: ${dir.content}. Generate JSON action.`;
159
+ const res = await complete(prompt);
160
+ try {
161
+ return JSON.parse(res);
162
+ } catch {
163
+ return { type: "idle", reason: res };
164
+ }
165
+ };
166
+ return {
167
+ init: init2,
168
+ complete,
169
+ completeStream,
170
+ processObservation,
171
+ generateAction,
172
+ embed
173
+ // Extended interface for private use by Memory
174
+ };
121
175
  };
176
+ var createCortex = (config) => createNativeCortex(config);
122
177
 
123
178
  // src/agent.ts
124
179
  var createInitialState = (partial) => {
@@ -161,19 +216,19 @@ var exportToSoul = (agentId, name, persona, state, memories) => {
161
216
  };
162
217
  };
163
218
  var createAgent = (config) => {
164
- return new AgentImpl(config);
165
- };
166
- var AgentImpl = class {
167
- constructor(config) {
168
- this.config = config;
169
- this.cortex = config.cortex;
170
- this.state = createInitialState(config.initialState);
171
- this.memories = [];
172
- }
173
- async process(input, context = {}) {
174
- const currentState = this.getState();
219
+ let state = createInitialState(config.initialState);
220
+ let memories = [];
221
+ const cortex = config.cortex;
222
+ const apiUrl = config.apiUrl || "http://localhost:8080";
223
+ const getAgentState = () => {
224
+ return { ...state };
225
+ };
226
+ const setAgentState = (newState) => {
227
+ state = newState;
228
+ };
229
+ const process2 = async (input, context = {}) => {
230
+ const currentState = getAgentState();
175
231
  const hp = currentState.hp || 100;
176
- const apiUrl = this.config.apiUrl || "http://localhost:8080";
177
232
  const apiContext = Object.entries(context).map(([k, v]) => [k, String(v)]);
178
233
  apiContext.push(["hp", String(hp)]);
179
234
  let directive = "Respond normally.";
@@ -201,227 +256,348 @@ var AgentImpl = class {
201
256
 
202
257
  User: ${input}
203
258
  Agent:`;
204
- const generatedText = await this.cortex.complete(prompt);
259
+ const generatedText = await cortex.complete(prompt);
205
260
  return {
206
261
  dialogue: generatedText,
207
262
  action: { type: instruction, reason: directive },
208
263
  thought: `Directive: ${directive}`
209
264
  };
210
- }
211
- getState() {
212
- return { ...this.state };
213
- }
214
- setState(newState) {
215
- this.state = newState;
216
- }
217
- export() {
265
+ };
266
+ const exportSoul = () => {
218
267
  return exportToSoul(
219
268
  "agent-" + Math.random().toString(36).substring(7),
220
269
  "Agent",
221
- this.config.persona,
222
- this.state,
223
- this.memories
270
+ config.persona,
271
+ state,
272
+ memories
224
273
  );
225
- }
274
+ };
275
+ return {
276
+ process: process2,
277
+ getState: getAgentState,
278
+ setState: setAgentState,
279
+ export: exportSoul
280
+ };
226
281
  };
227
- var fromSoul = (soul, cortex) => {
282
+ var fromSoul = (soul, cortex, memory) => {
228
283
  const agent = createAgent({
284
+ id: soul.id,
229
285
  cortex,
286
+ memory: memory || null,
230
287
  persona: soul.persona,
231
288
  initialState: soul.state
232
289
  });
233
290
  return agent;
234
291
  };
235
292
 
236
- // src/memory.ts
237
- var createMemoryItem = (text, type = "observation", importance = 0.5) => {
238
- return {
239
- id: `mem_${Date.now()}_${Math.random().toString(36).substring(7)}`,
240
- text,
241
- timestamp: Date.now(),
242
- type,
243
- importance: Math.max(0, Math.min(1, importance))
244
- // Clamp 0-1
245
- };
293
+ // src/soul.ts
294
+ var heliaNode = null;
295
+ var heliaJson = null;
296
+ var getHelia = async () => {
297
+ if (heliaNode) return { node: heliaNode, j: heliaJson };
298
+ try {
299
+ const { createHelia } = await import("helia");
300
+ const { json } = await import("@helia/json");
301
+ const { MemoryBlockstore } = await import("blockstore-core");
302
+ const blockstore = new MemoryBlockstore();
303
+ heliaNode = await createHelia({ blockstore });
304
+ heliaJson = json(heliaNode);
305
+ console.log("> Helia IPFS Node Initialized:", heliaNode.libp2p.peerId.toString());
306
+ return { node: heliaNode, j: heliaJson };
307
+ } catch (e) {
308
+ console.warn("Failed to init Helia (IPFS):", e);
309
+ return null;
310
+ }
246
311
  };
247
- var applyTemporalDecay = (memory, currentTime, decayRate = 1e-3) => {
248
- const ageMs = currentTime - memory.timestamp;
249
- const ageHours = ageMs / (1e3 * 60 * 60);
250
- const decayFactor = Math.exp(-decayRate * ageHours);
312
+ var createSoul = (id, name, persona, state, memories = []) => {
251
313
  return {
252
- ...memory,
253
- importance: memory.importance * decayFactor
314
+ id,
315
+ version: "1.0.0",
316
+ name,
317
+ persona,
318
+ state: { ...state },
319
+ memories: [...memories]
254
320
  };
255
321
  };
256
- var computeSimilarity = (text1, text2) => {
257
- const words1 = new Set(text1.toLowerCase().split(/\s+/));
258
- const words2 = new Set(text2.toLowerCase().split(/\s+/));
259
- let intersection = 0;
260
- words1.forEach((word) => {
261
- if (words2.has(word)) intersection++;
262
- });
263
- const union = words1.size + words2.size - intersection;
264
- return union > 0 ? intersection / union : 0;
322
+ var serializeSoul = (soul) => {
323
+ return JSON.stringify(soul, null, 2);
265
324
  };
266
- var rankMemoriesByRelevance = (memories, query, limit = 5) => {
267
- const scored = memories.map((mem) => ({
268
- memory: mem,
269
- score: computeSimilarity(mem.text, query) * mem.importance
270
- }));
271
- return scored.sort((a, b) => b.score - a.score).slice(0, limit).map((s) => s.memory);
325
+ var deserializeSoul = (json) => {
326
+ const parsed = JSON.parse(json);
327
+ if (!parsed.id || !parsed.persona || !parsed.state) {
328
+ throw new Error("Invalid Soul format: missing required fields");
329
+ }
330
+ return {
331
+ id: parsed.id,
332
+ version: parsed.version || "1.0.0",
333
+ name: parsed.name || "Unknown",
334
+ persona: parsed.persona,
335
+ state: parsed.state,
336
+ memories: parsed.memories || [],
337
+ signature: parsed.signature
338
+ };
272
339
  };
273
- var createMemory = (config = {}) => {
274
- return new LocalMemory(config);
340
+ var validateSoul = (soul) => {
341
+ const errors = [];
342
+ if (!soul.id) errors.push("Missing id");
343
+ if (!soul.persona) errors.push("Missing persona");
344
+ if (!soul.state) errors.push("Missing state");
345
+ if (!soul.state?.mood) errors.push("Missing state.mood");
346
+ return {
347
+ valid: errors.length === 0,
348
+ errors
349
+ };
275
350
  };
276
- var LocalMemory = class {
277
- constructor(config) {
278
- this.config = {
279
- decay: config.decay ?? "none",
280
- maxContextWindow: config.maxContextWindow ?? 10,
281
- storageKey: config.storageKey ?? "forbocai_memory",
282
- ...config
283
- };
284
- this.memories = [];
285
- this.loadFromStorage();
286
- }
287
- /**
288
- * Store a new memory observation
289
- */
290
- async store(text, type = "observation", importance = 0.5) {
291
- const memory = createMemoryItem(text, type, importance);
292
- this.memories = [...this.memories, memory];
293
- this.saveToStorage();
294
- if (this.config.apiUrl && this.config.agentId) {
351
+ var exportSoulToIPFS = async (agentId, soul, config = {}) => {
352
+ if (config.useLocalNode !== false) {
353
+ const helia = await getHelia();
354
+ if (helia) {
295
355
  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
- });
356
+ const cid = await helia.j.add(soul);
357
+ return {
358
+ cid: cid.toString(),
359
+ ipfsUrl: `ipfs://${cid.toString()}`,
360
+ soul
361
+ };
304
362
  } catch (e) {
305
- console.warn("Failed to sync memory to API:", e);
363
+ console.warn("Helia add failed, falling back to API", e);
306
364
  }
307
365
  }
308
- return memory;
309
366
  }
310
- /**
311
- * Recall memories relevant to a query (semantic search)
312
- */
313
- async recall(query, limit = 5) {
314
- let processedMemories = this.memories;
315
- if (this.config.decay === "temporal") {
316
- const now = Date.now();
317
- processedMemories = this.memories.map(
318
- (mem) => applyTemporalDecay(mem, now)
319
- );
367
+ const apiUrl = config.apiUrl || "https://forbocai-api.onrender.com";
368
+ try {
369
+ const response = await fetch(`${apiUrl}/agents/${agentId}/soul/export`, {
370
+ method: "POST",
371
+ headers: { "Content-Type": "application/json" },
372
+ body: JSON.stringify({ agentIdRef: agentId })
373
+ });
374
+ if (!response.ok) {
375
+ throw new Error(`Export failed: ${response.statusText}`);
320
376
  }
321
- const results = rankMemoriesByRelevance(
322
- processedMemories,
323
- query,
324
- limit
325
- );
326
- if (this.config.apiUrl && this.config.agentId) {
377
+ const data = await response.json();
378
+ return {
379
+ cid: data.cid,
380
+ ipfsUrl: data.ipfsUrl,
381
+ signature: data.signature,
382
+ soul
383
+ };
384
+ } catch (e) {
385
+ const mockCid = `Qm${Buffer.from(soul.id).toString("base64").substring(0, 44)}`;
386
+ return {
387
+ cid: mockCid,
388
+ ipfsUrl: `ipfs://${mockCid}`,
389
+ soul
390
+ };
391
+ }
392
+ };
393
+ var importSoulFromIPFS = async (cid, config = {}) => {
394
+ if (config.useLocalNode !== false) {
395
+ const helia = await getHelia();
396
+ if (helia) {
327
397
  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
- }
340
398
  } catch (e) {
341
- console.warn("Failed to recall from API:", e);
342
399
  }
343
400
  }
344
- return results;
345
- }
346
- /**
347
- * List all memories (paginated)
348
- */
349
- async list(limit = 50, offset = 0) {
350
- return this.memories.slice(offset, offset + limit).map((m) => ({ ...m }));
351
- }
352
- /**
353
- * Clear all memories
354
- */
355
- async clear() {
356
- this.memories = [];
357
- this.saveToStorage();
358
401
  }
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;
379
- 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);
402
+ const apiUrl = config.apiUrl || "https://forbocai-api.onrender.com";
403
+ try {
404
+ const response = await fetch(`${apiUrl}/souls/${cid}`, {
405
+ method: "GET",
406
+ headers: { "Content-Type": "application/json" }
407
+ });
408
+ if (!response.ok) {
409
+ throw new Error(`Import failed: ${response.statusText}`);
386
410
  }
411
+ const data = await response.json();
412
+ return {
413
+ id: data.soulId,
414
+ version: "1.0.0",
415
+ name: data.soulName,
416
+ persona: data.dna,
417
+ state: { mood: "neutral", inventory: [], skills: {}, relationships: {} },
418
+ memories: []
419
+ };
420
+ } catch (e) {
421
+ throw new Error(`Failed to import Soul from CID ${cid}: ${e}`);
387
422
  }
388
- /**
389
- * Save memories to localStorage (browser) or skip (Node)
390
- */
391
- saveToStorage() {
392
- if (typeof window === "undefined" || !window.localStorage) return;
393
- try {
394
- localStorage.setItem(this.config.storageKey, JSON.stringify(this.memories));
395
- } catch (e) {
396
- console.warn("Failed to save memories to storage:", e);
423
+ };
424
+ var getSoulList = async (limit = 50, apiUrl) => {
425
+ const url = apiUrl || "https://forbocai-api.onrender.com";
426
+ try {
427
+ const response = await fetch(`${url}/souls?limit=${limit}`, {
428
+ method: "GET",
429
+ headers: { "Content-Type": "application/json" }
430
+ });
431
+ if (!response.ok) {
432
+ throw new Error(`Failed to get Soul list: ${response.statusText}`);
397
433
  }
434
+ const data = await response.json();
435
+ return data.souls.map((s) => ({
436
+ cid: s.cid || s.soulId,
437
+ name: s.name || s.soulName,
438
+ agentId: s.agentId,
439
+ exportedAt: s.exportedAt || s.createdAt,
440
+ ipfsUrl: s.ipfsUrl || `ipfs://${s.cid || s.soulId}`
441
+ }));
442
+ } catch (e) {
443
+ return [];
398
444
  }
399
445
  };
400
- var MockMemory = class {
401
- constructor() {
402
- this.memories = [];
403
- }
404
- async store(text, type, importance) {
405
- const memory = createMemoryItem(text, type, importance);
406
- this.memories.push(memory);
407
- return memory;
408
- }
409
- async recall(query, limit) {
410
- return rankMemoriesByRelevance(this.memories, query, limit);
411
- }
412
- async list(limit, offset) {
413
- return this.memories.slice(offset ?? 0, (offset ?? 0) + (limit ?? 50));
414
- }
415
- async clear() {
416
- this.memories = [];
446
+ var createAgentFromSoul = async (cid, cortexId, config = {}) => {
447
+ const apiUrl = config.apiUrl || "https://forbocai-api.onrender.com";
448
+ const response = await fetch(`${apiUrl}/agents/import`, {
449
+ method: "POST",
450
+ headers: { "Content-Type": "application/json" },
451
+ body: JSON.stringify({ cidRef: cid })
452
+ });
453
+ if (!response.ok) {
454
+ throw new Error(`Failed to create agent from Soul: ${response.statusText}`);
417
455
  }
418
- export() {
419
- return [...this.memories];
456
+ const data = await response.json();
457
+ return {
458
+ agentId: data.agentId,
459
+ persona: data.persona
460
+ };
461
+ };
462
+ var createSoulInstance = (id, name, persona, state, memories = [], initialApiUrl) => {
463
+ const soulData = createSoul(id, name, persona, state, memories);
464
+ const defaultApiUrl = initialApiUrl || "https://forbocai-api.onrender.com";
465
+ const exportSoul = async (config) => {
466
+ return exportSoulToIPFS(soulData.id, soulData, {
467
+ ...config,
468
+ apiUrl: config?.apiUrl || defaultApiUrl
469
+ });
470
+ };
471
+ const toJSON = () => {
472
+ return { ...soulData };
473
+ };
474
+ return {
475
+ export: exportSoul,
476
+ toJSON
477
+ };
478
+ };
479
+
480
+ // src/memory.ts
481
+ import * as path3 from "path";
482
+
483
+ // src/vector.ts
484
+ import * as path2 from "path";
485
+ import * as fs2 from "fs";
486
+ var pipeline;
487
+ var lancedb;
488
+ var initVectorEngine = async () => {
489
+ if (pipeline) return;
490
+ try {
491
+ console.log("> Initializing Vector Engine (Transformers)...");
492
+ const transformers = await import("@xenova/transformers");
493
+ transformers.env.cacheDir = path2.join(process.env.HOME || ".", ".forbocai", "cache");
494
+ pipeline = await transformers.pipeline("feature-extraction", "Xenova/all-MiniLM-L6-v2");
495
+ console.log("> Initializing LanceDB...");
496
+ lancedb = await import("@lancedb/lancedb");
497
+ } catch (e) {
498
+ console.error("Failed to init Vector Engine:", e);
420
499
  }
421
- async import(memories) {
422
- this.memories = [...this.memories, ...memories];
500
+ };
501
+ var generateEmbedding = async (text) => {
502
+ if (!pipeline) await initVectorEngine();
503
+ const output = await pipeline(text, { pooling: "mean", normalize: true });
504
+ return Array.from(output.data);
505
+ };
506
+ var getVectorTable = async (dbPath, tableName = "memories") => {
507
+ if (!lancedb) await initVectorEngine();
508
+ if (!fs2.existsSync(dbPath)) fs2.mkdirSync(dbPath, { recursive: true });
509
+ const db = await lancedb.connect(dbPath);
510
+ const tables = await db.tableNames();
511
+ if (tables.includes(tableName)) {
512
+ return await db.openTable(tableName);
513
+ } else {
514
+ return null;
423
515
  }
424
516
  };
517
+ var createTable = async (dbPath, tableName, data) => {
518
+ if (!lancedb) await initVectorEngine();
519
+ const db = await lancedb.connect(dbPath);
520
+ return await db.createTable(tableName, data);
521
+ };
522
+
523
+ // src/memory.ts
524
+ var createMemoryItem = (text, type = "observation", importance = 0.5) => ({
525
+ id: `mem_${Date.now()}_${Math.random().toString(36).substring(7)}`,
526
+ text,
527
+ timestamp: Date.now(),
528
+ type,
529
+ importance: Math.min(1, Math.max(0, importance))
530
+ });
531
+ var createLanceDBMemory = (config) => {
532
+ const dbName = config.storageKey || "forbocai_vectors";
533
+ const dbPath = path3.join(process.cwd(), "local_infrastructure", "vectors", dbName + ".lance");
534
+ const tableName = "memories";
535
+ let _table = null;
536
+ const getTable = async () => {
537
+ if (_table) return _table;
538
+ _table = await getVectorTable(dbPath, tableName);
539
+ return _table;
540
+ };
541
+ const store = async (text, type = "observation", importance = 0.5) => {
542
+ const item = createMemoryItem(text, type, importance);
543
+ const vector = await generateEmbedding(text);
544
+ const record = {
545
+ ...item,
546
+ vector
547
+ };
548
+ const existingTable = await getTable();
549
+ if (existingTable) {
550
+ await existingTable.add([record]);
551
+ } else {
552
+ _table = await createTable(dbPath, tableName, [record]);
553
+ }
554
+ return item;
555
+ };
556
+ const recall = async (query, limit = 5) => {
557
+ const table = await getTable();
558
+ if (!table) return [];
559
+ const queryVec = await generateEmbedding(query);
560
+ const results = await table.search(queryVec).limit(limit).execute();
561
+ return results.map((r) => ({
562
+ id: r.id,
563
+ text: r.text,
564
+ timestamp: r.timestamp,
565
+ type: r.type,
566
+ importance: r.importance
567
+ }));
568
+ };
569
+ const list = async (limit = 50, offset = 0) => {
570
+ const table = await getTable();
571
+ if (!table) return [];
572
+ const results = await table.query().limit(limit + offset).execute();
573
+ return results.slice(offset).map((r) => ({
574
+ id: r.id,
575
+ text: r.text,
576
+ timestamp: r.timestamp,
577
+ type: r.type,
578
+ importance: r.importance
579
+ }));
580
+ };
581
+ const clear = async () => {
582
+ const tablePath = path3.join(dbPath, tableName + ".lance");
583
+ try {
584
+ const fs3 = await import("fs");
585
+ if (fs3.existsSync(tablePath)) {
586
+ fs3.rmSync(tablePath, { recursive: true, force: true });
587
+ }
588
+ if (fs3.existsSync(dbPath)) {
589
+ fs3.rmSync(dbPath, { recursive: true, force: true });
590
+ }
591
+ _table = null;
592
+ console.log("> Memory cleared successfully");
593
+ } catch (e) {
594
+ console.error("Failed to clear memory:", e);
595
+ throw e;
596
+ }
597
+ };
598
+ return { store, recall, list, clear };
599
+ };
600
+ var createMemory = (config = {}) => createLanceDBMemory(config);
425
601
 
426
602
  // src/bridge.ts
427
603
  var movementRule = {
@@ -592,30 +768,40 @@ var DEFAULT_RULES = [
592
768
  resourceRule
593
769
  ];
594
770
  var createBridge = (config = {}) => {
595
- return new BridgeImpl(config);
596
- };
597
- var BridgeImpl = class {
598
- constructor(config) {
599
- this.config = {
600
- strictMode: config.strictMode ?? false,
601
- ...config
602
- };
603
- this.rules = /* @__PURE__ */ new Map();
604
- DEFAULT_RULES.forEach((rule) => this.rules.set(rule.id, rule));
605
- if (config.customRules) {
606
- config.customRules.forEach((rule) => this.rules.set(rule.id, rule));
607
- }
771
+ const effectiveConfig = {
772
+ strictMode: config.strictMode ?? false,
773
+ ...config
774
+ };
775
+ const rules = /* @__PURE__ */ new Map();
776
+ DEFAULT_RULES.forEach((rule) => rules.set(rule.id, rule));
777
+ if (config.customRules) {
778
+ config.customRules.forEach((rule) => rules.set(rule.id, rule));
608
779
  }
609
- /**
610
- * Validate an action against all applicable rules
611
- */
612
- async validate(action, context = {}) {
613
- const applicableRules = Array.from(this.rules.values()).filter(
780
+ const validateRemote = async (action) => {
781
+ const response = await fetch(`${effectiveConfig.apiUrl}/bridge/validate`, {
782
+ method: "POST",
783
+ headers: { "Content-Type": "application/json" },
784
+ body: JSON.stringify({
785
+ actionType: action.type,
786
+ payload: JSON.stringify(action.payload || {})
787
+ })
788
+ });
789
+ if (!response.ok) {
790
+ throw new Error(`API validation failed: ${response.statusText}`);
791
+ }
792
+ const data = await response.json();
793
+ return {
794
+ valid: data.valid,
795
+ reason: data.reason
796
+ };
797
+ };
798
+ const validate = async (action, context = {}) => {
799
+ const applicableRules = Array.from(rules.values()).filter(
614
800
  (rule) => rule.actionTypes.some(
615
801
  (t) => t.toLowerCase() === action.type.toLowerCase()
616
802
  )
617
803
  );
618
- if (this.config.strictMode && applicableRules.length === 0) {
804
+ if (effectiveConfig.strictMode && applicableRules.length === 0) {
619
805
  const safeTypes = ["IDLE", "idle", "WAIT", "wait"];
620
806
  if (!safeTypes.includes(action.type)) {
621
807
  return {
@@ -629,9 +815,9 @@ var BridgeImpl = class {
629
815
  for (const rule of applicableRules) {
630
816
  const result = rule.validate(currentAction, context);
631
817
  if (!result.valid) {
632
- if (this.config.apiUrl) {
818
+ if (effectiveConfig.apiUrl) {
633
819
  try {
634
- const apiResult = await this.validateRemote(action);
820
+ const apiResult = await validateRemote(action);
635
821
  console.debug("API validation result:", apiResult);
636
822
  } catch (e) {
637
823
  console.warn("Failed to validate via API:", e);
@@ -647,59 +833,22 @@ var BridgeImpl = class {
647
833
  valid: true,
648
834
  correctedAction: currentAction !== action ? currentAction : void 0
649
835
  };
650
- }
651
- /**
652
- * Validate action via remote API
653
- */
654
- async validateRemote(action) {
655
- const response = await fetch(`${this.config.apiUrl}/bridge/validate`, {
656
- method: "POST",
657
- headers: { "Content-Type": "application/json" },
658
- body: JSON.stringify({
659
- actionType: action.type,
660
- payload: JSON.stringify(action.payload || {})
661
- })
662
- });
663
- if (!response.ok) {
664
- throw new Error(`API validation failed: ${response.statusText}`);
665
- }
666
- const data = await response.json();
667
- return {
668
- valid: data.valid,
669
- reason: data.reason
670
- };
671
- }
672
- /**
673
- * Register a custom validation rule
674
- */
675
- registerRule(rule) {
676
- this.rules.set(rule.id, rule);
677
- }
678
- /**
679
- * List all registered rules
680
- */
681
- listRules() {
682
- return Array.from(this.rules.values());
683
- }
684
- /**
685
- * Remove a rule by ID
686
- */
687
- removeRule(ruleId) {
688
- return this.rules.delete(ruleId);
689
- }
690
- };
691
- var MockBridge = class {
692
- async validate(action) {
693
- return { valid: true };
694
- }
695
- registerRule(_rule) {
696
- }
697
- listRules() {
698
- return [];
699
- }
700
- removeRule(_ruleId) {
701
- return false;
702
- }
836
+ };
837
+ const registerRule = (rule) => {
838
+ rules.set(rule.id, rule);
839
+ };
840
+ const listRules = () => {
841
+ return Array.from(rules.values());
842
+ };
843
+ const removeRule = (ruleId) => {
844
+ return rules.delete(ruleId);
845
+ };
846
+ return {
847
+ validate,
848
+ registerRule,
849
+ listRules,
850
+ removeRule
851
+ };
703
852
  };
704
853
  var validateAction = (action, rules, context = {}) => {
705
854
  for (const rule of rules) {
@@ -713,132 +862,6 @@ var validateAction = (action, rules, context = {}) => {
713
862
  return { valid: true };
714
863
  };
715
864
 
716
- // src/soul.ts
717
- var createSoul = (id, name, persona, state, memories = []) => {
718
- return {
719
- id,
720
- version: "1.0.0",
721
- name,
722
- persona,
723
- state: { ...state },
724
- memories: [...memories]
725
- };
726
- };
727
- var serializeSoul = (soul) => {
728
- return JSON.stringify(soul, null, 2);
729
- };
730
- var deserializeSoul = (json) => {
731
- const parsed = JSON.parse(json);
732
- if (!parsed.id || !parsed.persona || !parsed.state) {
733
- throw new Error("Invalid Soul format: missing required fields");
734
- }
735
- return {
736
- id: parsed.id,
737
- version: parsed.version || "1.0.0",
738
- name: parsed.name || "Unknown",
739
- persona: parsed.persona,
740
- state: parsed.state,
741
- memories: parsed.memories || [],
742
- signature: parsed.signature
743
- };
744
- };
745
- var validateSoul = (soul) => {
746
- const errors = [];
747
- if (!soul.id) errors.push("Missing id");
748
- if (!soul.persona) errors.push("Missing persona");
749
- if (!soul.state) errors.push("Missing state");
750
- if (!soul.state?.mood) errors.push("Missing state.mood");
751
- return {
752
- valid: errors.length === 0,
753
- errors
754
- };
755
- };
756
- var exportSoulToIPFS = async (agentId, soul, config = {}) => {
757
- const apiUrl = config.apiUrl || "https://forbocai-api.onrender.com";
758
- try {
759
- const response = await fetch(`${apiUrl}/agents/${agentId}/soul/export`, {
760
- method: "POST",
761
- headers: { "Content-Type": "application/json" },
762
- body: JSON.stringify({ agentIdRef: agentId })
763
- });
764
- if (!response.ok) {
765
- throw new Error(`Export failed: ${response.statusText}`);
766
- }
767
- const data = await response.json();
768
- return {
769
- cid: data.cid,
770
- ipfsUrl: data.ipfsUrl,
771
- signature: data.signature,
772
- soul
773
- };
774
- } catch (e) {
775
- const mockCid = `Qm${Buffer.from(soul.id).toString("base64").substring(0, 44)}`;
776
- return {
777
- cid: mockCid,
778
- ipfsUrl: `ipfs://${mockCid}`,
779
- soul
780
- };
781
- }
782
- };
783
- var importSoulFromIPFS = async (cid, config = {}) => {
784
- const apiUrl = config.apiUrl || "https://forbocai-api.onrender.com";
785
- try {
786
- const response = await fetch(`${apiUrl}/souls/${cid}`, {
787
- method: "GET",
788
- headers: { "Content-Type": "application/json" }
789
- });
790
- if (!response.ok) {
791
- throw new Error(`Import failed: ${response.statusText}`);
792
- }
793
- const data = await response.json();
794
- return {
795
- id: data.soulId,
796
- version: "1.0.0",
797
- name: data.soulName,
798
- persona: data.dna,
799
- // DNA contains the persona
800
- state: { mood: "neutral", inventory: [], skills: {}, relationships: {} },
801
- memories: []
802
- };
803
- } catch (e) {
804
- throw new Error(`Failed to import Soul from CID ${cid}: ${e}`);
805
- }
806
- };
807
- var createAgentFromSoul = async (cid, cortexId, config = {}) => {
808
- const apiUrl = config.apiUrl || "https://forbocai-api.onrender.com";
809
- const response = await fetch(`${apiUrl}/agents/import`, {
810
- method: "POST",
811
- headers: { "Content-Type": "application/json" },
812
- body: JSON.stringify({ cidRef: cid })
813
- });
814
- if (!response.ok) {
815
- throw new Error(`Failed to create agent from Soul: ${response.statusText}`);
816
- }
817
- const data = await response.json();
818
- return {
819
- agentId: data.agentId,
820
- persona: data.persona
821
- };
822
- };
823
- var SoulImpl = class {
824
- constructor(id, name, persona, state, memories = [], apiUrl) {
825
- this.soul = createSoul(id, name, persona, state, memories);
826
- this.apiUrl = apiUrl || "https://forbocai-api.onrender.com";
827
- }
828
- async export(config) {
829
- return exportSoulToIPFS(this.soul.id, this.soul, {
830
- ...config,
831
- apiUrl: config?.apiUrl || this.apiUrl
832
- });
833
- }
834
- toJSON() {
835
- return { ...this.soul };
836
- }
837
- };
838
- var createSoulInstance = (id, name, persona, state, memories, apiUrl) => {
839
- return new SoulImpl(id, name, persona, state, memories, apiUrl);
840
- };
841
-
842
865
  // src/ghost.ts
843
866
  var startGhostSession = async (config) => {
844
867
  const apiUrl = config.apiUrl || "https://forbocai-api.onrender.com";
@@ -964,79 +987,118 @@ var waitForGhostCompletion = async (sessionId, pollIntervalMs = 5e3, timeoutMs =
964
987
  }
965
988
  throw new Error(`Ghost session timed out after ${timeoutMs}ms`);
966
989
  };
967
- var GhostSession = class {
968
- constructor(config) {
969
- this.sessionId = null;
970
- this.config = config;
971
- this.apiUrl = config.apiUrl || "https://forbocai-api.onrender.com";
990
+ var stopGhostSession = async (sessionId, apiUrl) => {
991
+ const url = apiUrl || "https://forbocai-api.onrender.com";
992
+ try {
993
+ const response = await fetch(`${url}/ghost/${sessionId}/stop`, {
994
+ method: "POST",
995
+ headers: { "Content-Type": "application/json" }
996
+ });
997
+ if (!response.ok) {
998
+ throw new Error(`Failed to stop Ghost session: ${response.statusText}`);
999
+ }
1000
+ const data = await response.json();
1001
+ return {
1002
+ stopped: true,
1003
+ status: data.status || "stopped"
1004
+ };
1005
+ } catch (e) {
1006
+ return {
1007
+ stopped: true,
1008
+ status: "stopped"
1009
+ };
972
1010
  }
973
- async run() {
974
- const result = await startGhostSession(this.config);
975
- this.sessionId = result.sessionId;
976
- return this.sessionId;
1011
+ };
1012
+ var getGhostHistory = async (limit = 10, apiUrl) => {
1013
+ const url = apiUrl || "https://forbocai-api.onrender.com";
1014
+ try {
1015
+ const response = await fetch(`${url}/ghost/history?limit=${limit}`, {
1016
+ method: "GET",
1017
+ headers: { "Content-Type": "application/json" }
1018
+ });
1019
+ if (!response.ok) {
1020
+ throw new Error(`Failed to get Ghost history: ${response.statusText}`);
1021
+ }
1022
+ const data = await response.json();
1023
+ return data.sessions.map((s) => ({
1024
+ sessionId: s.sessionId,
1025
+ testSuite: s.testSuite,
1026
+ startedAt: s.startedAt,
1027
+ completedAt: s.completedAt,
1028
+ status: s.status,
1029
+ passRate: s.passRate
1030
+ }));
1031
+ } catch (e) {
1032
+ return [];
977
1033
  }
978
- async status() {
979
- if (!this.sessionId) {
1034
+ };
1035
+ var createGhost = (config) => {
1036
+ let sessionId = null;
1037
+ const apiUrl = config.apiUrl || "https://forbocai-api.onrender.com";
1038
+ const run = async () => {
1039
+ const result = await startGhostSession(config);
1040
+ sessionId = result.sessionId;
1041
+ return sessionId;
1042
+ };
1043
+ const status = async () => {
1044
+ if (!sessionId) {
980
1045
  throw new Error("Ghost session not started");
981
1046
  }
982
- return getGhostStatus(this.sessionId, this.apiUrl);
983
- }
984
- async results() {
985
- if (!this.sessionId) {
1047
+ return getGhostStatus(sessionId, apiUrl);
1048
+ };
1049
+ const results = async () => {
1050
+ if (!sessionId) {
986
1051
  throw new Error("Ghost session not started");
987
1052
  }
988
- return getGhostResults(this.sessionId, this.apiUrl);
989
- }
990
- async stop() {
991
- this.sessionId = null;
992
- }
993
- async waitForCompletion(pollIntervalMs, timeoutMs, onProgress) {
994
- if (!this.sessionId) {
1053
+ return getGhostResults(sessionId, apiUrl);
1054
+ };
1055
+ const stop = async () => {
1056
+ sessionId = null;
1057
+ };
1058
+ const waitForCompletion = async (pollIntervalMs, timeoutMs, onProgress) => {
1059
+ if (!sessionId) {
995
1060
  throw new Error("Ghost session not started");
996
1061
  }
997
1062
  return waitForGhostCompletion(
998
- this.sessionId,
1063
+ sessionId,
999
1064
  pollIntervalMs,
1000
1065
  timeoutMs,
1001
- this.apiUrl,
1066
+ apiUrl,
1002
1067
  onProgress
1003
1068
  );
1004
- }
1005
- };
1006
- var createGhost = (config) => {
1007
- return new GhostSession(config);
1069
+ };
1070
+ return {
1071
+ run,
1072
+ status,
1073
+ results,
1074
+ stop
1075
+ // Helper method attached to the instance object, though not part of IGhost strictly in the original interface,
1076
+ // it was on the class. We should probably expand IGhost if this is needed, or just rely on the standalone function.
1077
+ // For now, I'll return it as an extra property or strictly adhere to IGhost.
1078
+ // The original Class had it. I'll omit it from the return to match IGhost exactly,
1079
+ // or check IGhost definition. IGhost definition (lines 62-67) does NOT have waitForCompletion.
1080
+ // So I will omit it to match the Interface.
1081
+ };
1008
1082
  };
1009
1083
 
1010
1084
  // src/index.ts
1011
1085
  var init = () => {
1012
- const reset = "\x1B[0m";
1013
- const cyan = "\x1B[36m";
1014
- const blue = "\x1B[34m";
1015
- const dim = "\x1B[2m";
1016
- const bold = "\x1B[1m";
1017
- const runes = "\u16A0 \u16EB \u16DF \u16EB \u16B1 \u16EB \u16D2 \u16EB \u16DF \u16EB \u16B2";
1018
- const border = `${dim}----------------------------------------${reset}`;
1019
- console.error(`
1020
- ${border}
1021
- ${cyan}${bold}FORBOC AI SDK${reset} ${dim}v0.0.1${reset}
1022
- ${blue}${runes}${reset}
1023
- ${border}
1024
- ${dim}> Initializing Cortex...${reset}
1025
- ${dim}> Connecting into the Neuro-Symbolic Grid...${reset}
1026
- ${dim}> Status: ${cyan}ONLINE${reset}
1027
-
1028
- ${cyan}Welcome to the Future of NPC Intelligence.${reset}
1029
- `);
1086
+ console.log(`
1087
+ \x1B[36m _ _ _ ___ _ _
1088
+ | \\| |___ _ _ _ __ ___ / \\ |_ _|_ _ | |_ (_|
1089
+ | . / -_) || | '_ / _ \\/ _ \\ | || ' \\| _| | |
1090
+ |_|\\_\\___|\\_,_| ._\\___/_/ \\_\\ |___|_||_|\\__|_|_|
1091
+ |_| \x1B[0m
1092
+ Neuro-Symbolic Grid SDK v0.3.2
1093
+ ---------------------------------
1094
+ Vessel: ACTIVE
1095
+ Memory: LOCAL (LanceDB)
1096
+ Smart: LOCAL (GGUF)
1097
+ Grid: CONNECTED
1098
+ `);
1030
1099
  };
1031
1100
  export {
1032
- GhostSession,
1033
- MockBridge,
1034
- MockCortex,
1035
- MockMemory,
1036
- SoulImpl,
1037
- applyTemporalDecay,
1038
1101
  attackRule,
1039
- computeSimilarity,
1040
1102
  createAgent,
1041
1103
  createAgentFromSoul,
1042
1104
  createBridge,
@@ -1047,25 +1109,28 @@ export {
1047
1109
  createMemoryItem,
1048
1110
  createSoul,
1049
1111
  createSoulInstance,
1112
+ createTable,
1050
1113
  deserializeSoul,
1051
1114
  exportSoulToIPFS,
1052
1115
  exportToSoul,
1053
1116
  fromSoul,
1054
- generateActionFromDirective,
1055
- generateCompletion,
1117
+ generateEmbedding,
1118
+ getGhostHistory,
1056
1119
  getGhostResults,
1057
1120
  getGhostStatus,
1121
+ getSoulList,
1122
+ getVectorTable,
1058
1123
  importSoulFromIPFS,
1059
1124
  init,
1125
+ initVectorEngine,
1060
1126
  interactRule,
1061
1127
  movementRule,
1062
1128
  processAgentInput,
1063
- processObservationToDirective,
1064
- rankMemoriesByRelevance,
1065
1129
  resourceRule,
1066
1130
  serializeSoul,
1067
1131
  speakRule,
1068
1132
  startGhostSession,
1133
+ stopGhostSession,
1069
1134
  updateAgentState,
1070
1135
  validateAction,
1071
1136
  validateSoul,