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/README.md +88 -41
- package/dist/chunk-4FGRXBH3.mjs +1026 -0
- package/dist/cli.js +87 -22
- package/dist/cli.mjs +87 -22
- package/dist/index.d.mts +131 -193
- package/dist/index.d.ts +131 -193
- package/dist/index.js +634 -563
- package/dist/index.mjs +617 -552
- package/package.json +18 -7
- package/postinstall.js +27 -9
package/dist/index.mjs
CHANGED
|
@@ -1,124 +1,179 @@
|
|
|
1
1
|
// src/cortex.ts
|
|
2
|
-
|
|
3
|
-
|
|
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
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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
|
|
12
|
-
return {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
console.
|
|
51
|
-
|
|
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
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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: "
|
|
85
|
+
engine: "node-llama-cpp"
|
|
59
86
|
};
|
|
60
|
-
|
|
61
|
-
return
|
|
87
|
+
console.log("> Cortex Ready.");
|
|
88
|
+
return status;
|
|
62
89
|
} catch (e) {
|
|
63
|
-
console.
|
|
64
|
-
|
|
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
|
|
75
|
-
if (!
|
|
76
|
-
|
|
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
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
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
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
222
|
-
|
|
223
|
-
|
|
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/
|
|
237
|
-
var
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
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
|
|
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
|
-
|
|
253
|
-
|
|
314
|
+
id,
|
|
315
|
+
version: "1.0.0",
|
|
316
|
+
name,
|
|
317
|
+
persona,
|
|
318
|
+
state: { ...state },
|
|
319
|
+
memories: [...memories]
|
|
254
320
|
};
|
|
255
321
|
};
|
|
256
|
-
var
|
|
257
|
-
|
|
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
|
|
267
|
-
const
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
}
|
|
271
|
-
return
|
|
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
|
|
274
|
-
|
|
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
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
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
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
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("
|
|
363
|
+
console.warn("Helia add failed, falling back to API", e);
|
|
306
364
|
}
|
|
307
365
|
}
|
|
308
|
-
return memory;
|
|
309
366
|
}
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
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
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
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
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
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
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
}
|
|
396
|
-
|
|
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
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
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
|
-
|
|
419
|
-
|
|
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
|
-
|
|
422
|
-
|
|
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
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
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
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
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 (
|
|
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 (
|
|
818
|
+
if (effectiveConfig.apiUrl) {
|
|
633
819
|
try {
|
|
634
|
-
const apiResult = await
|
|
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
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
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
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
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
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
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
|
-
|
|
979
|
-
|
|
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(
|
|
983
|
-
}
|
|
984
|
-
async
|
|
985
|
-
if (!
|
|
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(
|
|
989
|
-
}
|
|
990
|
-
async
|
|
991
|
-
|
|
992
|
-
}
|
|
993
|
-
async
|
|
994
|
-
if (!
|
|
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
|
-
|
|
1063
|
+
sessionId,
|
|
999
1064
|
pollIntervalMs,
|
|
1000
1065
|
timeoutMs,
|
|
1001
|
-
|
|
1066
|
+
apiUrl,
|
|
1002
1067
|
onProgress
|
|
1003
1068
|
);
|
|
1004
|
-
}
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
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
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
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
|
-
|
|
1055
|
-
|
|
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,
|