forbocai 0.3.0 → 0.3.3
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 +63 -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 -197
- package/dist/index.d.ts +131 -197
- package/dist/index.js +636 -630
- package/dist/index.mjs +619 -619
- package/package.json +7 -2
- package/postinstall.js +27 -9
package/dist/index.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
8
|
var __export = (target, all) => {
|
|
7
9
|
for (var name in all)
|
|
@@ -15,19 +17,20 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
17
|
}
|
|
16
18
|
return to;
|
|
17
19
|
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
18
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
29
|
|
|
20
30
|
// src/index.ts
|
|
21
31
|
var index_exports = {};
|
|
22
32
|
__export(index_exports, {
|
|
23
|
-
GhostSession: () => GhostSession,
|
|
24
|
-
MockBridge: () => MockBridge,
|
|
25
|
-
MockCortex: () => MockCortex,
|
|
26
|
-
MockMemory: () => MockMemory,
|
|
27
|
-
SoulImpl: () => SoulImpl,
|
|
28
|
-
applyTemporalDecay: () => applyTemporalDecay,
|
|
29
33
|
attackRule: () => attackRule,
|
|
30
|
-
computeSimilarity: () => computeSimilarity,
|
|
31
34
|
createAgent: () => createAgent,
|
|
32
35
|
createAgentFromSoul: () => createAgentFromSoul,
|
|
33
36
|
createBridge: () => createBridge,
|
|
@@ -38,25 +41,28 @@ __export(index_exports, {
|
|
|
38
41
|
createMemoryItem: () => createMemoryItem,
|
|
39
42
|
createSoul: () => createSoul,
|
|
40
43
|
createSoulInstance: () => createSoulInstance,
|
|
44
|
+
createTable: () => createTable,
|
|
41
45
|
deserializeSoul: () => deserializeSoul,
|
|
42
46
|
exportSoulToIPFS: () => exportSoulToIPFS,
|
|
43
47
|
exportToSoul: () => exportToSoul,
|
|
44
48
|
fromSoul: () => fromSoul,
|
|
45
|
-
|
|
46
|
-
|
|
49
|
+
generateEmbedding: () => generateEmbedding,
|
|
50
|
+
getGhostHistory: () => getGhostHistory,
|
|
47
51
|
getGhostResults: () => getGhostResults,
|
|
48
52
|
getGhostStatus: () => getGhostStatus,
|
|
53
|
+
getSoulList: () => getSoulList,
|
|
54
|
+
getVectorTable: () => getVectorTable,
|
|
49
55
|
importSoulFromIPFS: () => importSoulFromIPFS,
|
|
50
56
|
init: () => init,
|
|
57
|
+
initVectorEngine: () => initVectorEngine,
|
|
51
58
|
interactRule: () => interactRule,
|
|
52
59
|
movementRule: () => movementRule,
|
|
53
60
|
processAgentInput: () => processAgentInput,
|
|
54
|
-
processObservationToDirective: () => processObservationToDirective,
|
|
55
|
-
rankMemoriesByRelevance: () => rankMemoriesByRelevance,
|
|
56
61
|
resourceRule: () => resourceRule,
|
|
57
62
|
serializeSoul: () => serializeSoul,
|
|
58
63
|
speakRule: () => speakRule,
|
|
59
64
|
startGhostSession: () => startGhostSession,
|
|
65
|
+
stopGhostSession: () => stopGhostSession,
|
|
60
66
|
updateAgentState: () => updateAgentState,
|
|
61
67
|
validateAction: () => validateAction,
|
|
62
68
|
validateSoul: () => validateSoul,
|
|
@@ -65,151 +71,181 @@ __export(index_exports, {
|
|
|
65
71
|
module.exports = __toCommonJS(index_exports);
|
|
66
72
|
|
|
67
73
|
// src/cortex.ts
|
|
68
|
-
var
|
|
69
|
-
var
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
"
|
|
74
|
+
var fs = __toESM(require("fs"));
|
|
75
|
+
var path = __toESM(require("path"));
|
|
76
|
+
var https = __toESM(require("https"));
|
|
77
|
+
var MODEL_URLS = {
|
|
78
|
+
"smollm2-135m": "https://huggingface.co/HuggingFaceTB/SmolLM2-135M-Instruct-GGUF/resolve/main/smollm2-135m-instruct-q4_k_m.gguf",
|
|
79
|
+
"llama3-8b": "https://huggingface.co/lmstudio-community/Meta-Llama-3-8B-Instruct-GGUF/resolve/main/Meta-Llama-3-8B-Instruct-Q4_K_M.gguf"
|
|
73
80
|
};
|
|
74
|
-
var
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
return `[Mock Response] Model: ${modelId}, Temp: ${temperature}. Response to: ${prompt}`;
|
|
81
|
+
var DEFAULT_MODEL = "smollm2-135m";
|
|
82
|
+
var ensureDirectoryExists = (dirPath) => {
|
|
83
|
+
if (!fs.existsSync(dirPath)) {
|
|
84
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
85
|
+
}
|
|
80
86
|
};
|
|
81
|
-
var
|
|
82
|
-
return {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
+
var downloadFile = (url, destPath) => {
|
|
88
|
+
return new Promise((resolve, reject) => {
|
|
89
|
+
const file = fs.createWriteStream(destPath);
|
|
90
|
+
https.get(url, (response) => {
|
|
91
|
+
if (response.statusCode === 302 || response.statusCode === 301) {
|
|
92
|
+
downloadFile(response.headers.location, destPath).then(resolve).catch(reject);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
if (response.statusCode !== 200) {
|
|
96
|
+
reject(new Error(`Failed to download: ${response.statusCode}`));
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
response.pipe(file);
|
|
100
|
+
file.on("finish", () => {
|
|
101
|
+
file.close();
|
|
102
|
+
resolve();
|
|
103
|
+
});
|
|
104
|
+
}).on("error", (err) => {
|
|
105
|
+
fs.unlink(destPath, () => {
|
|
106
|
+
});
|
|
107
|
+
reject(err);
|
|
108
|
+
});
|
|
109
|
+
});
|
|
87
110
|
};
|
|
88
|
-
var
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
111
|
+
var createNativeCortex = (config) => {
|
|
112
|
+
let status = {
|
|
113
|
+
id: "native-init",
|
|
114
|
+
model: config.model || DEFAULT_MODEL,
|
|
115
|
+
ready: false,
|
|
116
|
+
engine: "node-llama-cpp"
|
|
92
117
|
};
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
temperature: config.temperature ?? 0.7,
|
|
101
|
-
maxTokens: config.maxTokens ?? 1024,
|
|
102
|
-
gpu: config.gpu ?? true
|
|
103
|
-
};
|
|
104
|
-
}
|
|
105
|
-
async init() {
|
|
106
|
-
if (this.status?.ready) {
|
|
107
|
-
return this.status;
|
|
108
|
-
}
|
|
118
|
+
let llama;
|
|
119
|
+
let model;
|
|
120
|
+
let context;
|
|
121
|
+
let session;
|
|
122
|
+
const MODELS_DIR = path.join(process.cwd(), "local_infrastructure", "models");
|
|
123
|
+
const init2 = async () => {
|
|
124
|
+
if (status.ready) return status;
|
|
109
125
|
try {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
model: this.config.model,
|
|
122
|
-
ready: true,
|
|
123
|
-
engine: "webllm"
|
|
124
|
-
};
|
|
125
|
-
return this.status;
|
|
126
|
+
console.log("> Initializing Native Cortex (node-llama-cpp)...");
|
|
127
|
+
ensureDirectoryExists(MODELS_DIR);
|
|
128
|
+
const modelKey = config.model || DEFAULT_MODEL;
|
|
129
|
+
const modelUrl = MODEL_URLS[modelKey] || MODEL_URLS[DEFAULT_MODEL];
|
|
130
|
+
const modelFileName = path.basename(modelUrl);
|
|
131
|
+
const modelPath = path.join(MODELS_DIR, modelFileName);
|
|
132
|
+
if (!fs.existsSync(modelPath)) {
|
|
133
|
+
console.log(`> Downloading Model: ${modelKey}...`);
|
|
134
|
+
console.log(`> Source: ${modelUrl}`);
|
|
135
|
+
await downloadFile(modelUrl, modelPath);
|
|
136
|
+
console.log(`> Download complete.`);
|
|
126
137
|
} else {
|
|
127
|
-
console.
|
|
128
|
-
throw new Error("WebGPU unavailable");
|
|
138
|
+
console.log(`> Using cached model: ${modelPath}`);
|
|
129
139
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
140
|
+
const { getLlama, LlamaChatSession } = await import("node-llama-cpp");
|
|
141
|
+
llama = await getLlama();
|
|
142
|
+
console.log("> Loading model into memory...");
|
|
143
|
+
model = await llama.loadModel({
|
|
144
|
+
modelPath,
|
|
145
|
+
gpuLayers: config.gpu ? 99 : 0
|
|
146
|
+
// Try to use GPU if allowed
|
|
147
|
+
});
|
|
148
|
+
console.log("> Creating context...");
|
|
149
|
+
context = await model.createContext();
|
|
150
|
+
session = new LlamaChatSession({
|
|
151
|
+
contextSequence: context.getSequence()
|
|
152
|
+
});
|
|
153
|
+
status = {
|
|
154
|
+
id: `ctx_${Date.now()}`,
|
|
155
|
+
model: modelKey,
|
|
135
156
|
ready: true,
|
|
136
|
-
engine: "
|
|
157
|
+
engine: "node-llama-cpp"
|
|
137
158
|
};
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
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);
|
|
159
|
+
console.log("> Cortex Ready.");
|
|
160
|
+
return status;
|
|
161
|
+
} catch (e) {
|
|
162
|
+
console.error("Failed to initialize Native Cortex:", e);
|
|
163
|
+
throw e;
|
|
152
164
|
}
|
|
153
|
-
}
|
|
154
|
-
async
|
|
155
|
-
if (!
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
165
|
+
};
|
|
166
|
+
const complete = async (prompt, options = {}) => {
|
|
167
|
+
if (!status.ready) await init2();
|
|
168
|
+
return await session.prompt(prompt, {
|
|
169
|
+
maxTokens: options.maxTokens,
|
|
170
|
+
temperature: options.temperature
|
|
171
|
+
});
|
|
172
|
+
};
|
|
173
|
+
const completeStream = async function* (prompt, options = {}) {
|
|
174
|
+
if (!status.ready) await init2();
|
|
175
|
+
const tokenQueue = [];
|
|
176
|
+
let resolveNext = null;
|
|
177
|
+
let isComplete = false;
|
|
178
|
+
const generationPromise = session.prompt(prompt, {
|
|
179
|
+
maxTokens: options.maxTokens,
|
|
180
|
+
temperature: options.temperature,
|
|
181
|
+
onToken: (tokens) => {
|
|
182
|
+
try {
|
|
183
|
+
const text = model.detokenize(tokens);
|
|
184
|
+
if (text) {
|
|
185
|
+
tokenQueue.push(text);
|
|
186
|
+
if (resolveNext) {
|
|
187
|
+
resolveNext();
|
|
188
|
+
resolveNext = null;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
} catch {
|
|
192
|
+
tokenQueue.push(tokens.map((t) => String.fromCharCode(t % 256)).join(""));
|
|
193
|
+
}
|
|
166
194
|
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
195
|
+
}).then(() => {
|
|
196
|
+
isComplete = true;
|
|
197
|
+
if (resolveNext) resolveNext();
|
|
198
|
+
});
|
|
199
|
+
while (!isComplete || tokenQueue.length > 0) {
|
|
200
|
+
if (tokenQueue.length > 0) {
|
|
201
|
+
yield tokenQueue.shift();
|
|
202
|
+
} else if (!isComplete) {
|
|
203
|
+
await new Promise((resolve) => {
|
|
204
|
+
resolveNext = resolve;
|
|
205
|
+
});
|
|
172
206
|
}
|
|
173
207
|
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
208
|
+
await generationPromise;
|
|
209
|
+
};
|
|
210
|
+
const embed = async (text) => {
|
|
211
|
+
if (!status.ready) await init2();
|
|
212
|
+
try {
|
|
213
|
+
return new Array(384).fill(0).map(() => Math.random());
|
|
214
|
+
} catch (e) {
|
|
215
|
+
return [];
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
const processObservation = async (obs) => {
|
|
219
|
+
const prompt = `System: You are an agent.
|
|
220
|
+
Observation: ${obs.content}
|
|
221
|
+
Task: Generete a JSON directive { "type": "...", "content": "..." }.`;
|
|
222
|
+
const res = await complete(prompt);
|
|
223
|
+
try {
|
|
224
|
+
return JSON.parse(res);
|
|
225
|
+
} catch {
|
|
226
|
+
return { type: "thought", content: res };
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
const generateAction = async (dir) => {
|
|
230
|
+
const prompt = `Directive: ${dir.content}. Generate JSON action.`;
|
|
231
|
+
const res = await complete(prompt);
|
|
232
|
+
try {
|
|
233
|
+
return JSON.parse(res);
|
|
234
|
+
} catch {
|
|
235
|
+
return { type: "idle", reason: res };
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
return {
|
|
239
|
+
init: init2,
|
|
240
|
+
complete,
|
|
241
|
+
completeStream,
|
|
242
|
+
processObservation,
|
|
243
|
+
generateAction,
|
|
244
|
+
embed
|
|
245
|
+
// Extended interface for private use by Memory
|
|
246
|
+
};
|
|
212
247
|
};
|
|
248
|
+
var createCortex = (config) => createNativeCortex(config);
|
|
213
249
|
|
|
214
250
|
// src/agent.ts
|
|
215
251
|
var createInitialState = (partial) => {
|
|
@@ -252,19 +288,19 @@ var exportToSoul = (agentId, name, persona, state, memories) => {
|
|
|
252
288
|
};
|
|
253
289
|
};
|
|
254
290
|
var createAgent = (config) => {
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
291
|
+
let state = createInitialState(config.initialState);
|
|
292
|
+
let memories = [];
|
|
293
|
+
const cortex = config.cortex;
|
|
294
|
+
const apiUrl = config.apiUrl || "http://localhost:8080";
|
|
295
|
+
const getAgentState = () => {
|
|
296
|
+
return { ...state };
|
|
297
|
+
};
|
|
298
|
+
const setAgentState = (newState) => {
|
|
299
|
+
state = newState;
|
|
300
|
+
};
|
|
301
|
+
const process2 = async (input, context = {}) => {
|
|
302
|
+
const currentState = getAgentState();
|
|
266
303
|
const hp = currentState.hp || 100;
|
|
267
|
-
const apiUrl = this.config.apiUrl || "http://localhost:8080";
|
|
268
304
|
const apiContext = Object.entries(context).map(([k, v]) => [k, String(v)]);
|
|
269
305
|
apiContext.push(["hp", String(hp)]);
|
|
270
306
|
let directive = "Respond normally.";
|
|
@@ -292,227 +328,348 @@ var AgentImpl = class {
|
|
|
292
328
|
|
|
293
329
|
User: ${input}
|
|
294
330
|
Agent:`;
|
|
295
|
-
const generatedText = await
|
|
331
|
+
const generatedText = await cortex.complete(prompt);
|
|
296
332
|
return {
|
|
297
333
|
dialogue: generatedText,
|
|
298
334
|
action: { type: instruction, reason: directive },
|
|
299
335
|
thought: `Directive: ${directive}`
|
|
300
336
|
};
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
return { ...this.state };
|
|
304
|
-
}
|
|
305
|
-
setState(newState) {
|
|
306
|
-
this.state = newState;
|
|
307
|
-
}
|
|
308
|
-
export() {
|
|
337
|
+
};
|
|
338
|
+
const exportSoul = () => {
|
|
309
339
|
return exportToSoul(
|
|
310
340
|
"agent-" + Math.random().toString(36).substring(7),
|
|
311
341
|
"Agent",
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
342
|
+
config.persona,
|
|
343
|
+
state,
|
|
344
|
+
memories
|
|
315
345
|
);
|
|
316
|
-
}
|
|
346
|
+
};
|
|
347
|
+
return {
|
|
348
|
+
process: process2,
|
|
349
|
+
getState: getAgentState,
|
|
350
|
+
setState: setAgentState,
|
|
351
|
+
export: exportSoul
|
|
352
|
+
};
|
|
317
353
|
};
|
|
318
|
-
var fromSoul = (soul, cortex) => {
|
|
354
|
+
var fromSoul = (soul, cortex, memory) => {
|
|
319
355
|
const agent = createAgent({
|
|
356
|
+
id: soul.id,
|
|
320
357
|
cortex,
|
|
358
|
+
memory: memory || null,
|
|
321
359
|
persona: soul.persona,
|
|
322
360
|
initialState: soul.state
|
|
323
361
|
});
|
|
324
362
|
return agent;
|
|
325
363
|
};
|
|
326
364
|
|
|
327
|
-
// src/
|
|
328
|
-
var
|
|
329
|
-
var
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
365
|
+
// src/soul.ts
|
|
366
|
+
var heliaNode = null;
|
|
367
|
+
var heliaJson = null;
|
|
368
|
+
var getHelia = async () => {
|
|
369
|
+
if (heliaNode) return { node: heliaNode, j: heliaJson };
|
|
370
|
+
try {
|
|
371
|
+
const { createHelia } = await import("helia");
|
|
372
|
+
const { json } = await import("@helia/json");
|
|
373
|
+
const { MemoryBlockstore } = await import("blockstore-core");
|
|
374
|
+
const blockstore = new MemoryBlockstore();
|
|
375
|
+
heliaNode = await createHelia({ blockstore });
|
|
376
|
+
heliaJson = json(heliaNode);
|
|
377
|
+
console.log("> Helia IPFS Node Initialized:", heliaNode.libp2p.peerId.toString());
|
|
378
|
+
return { node: heliaNode, j: heliaJson };
|
|
379
|
+
} catch (e) {
|
|
380
|
+
console.warn("Failed to init Helia (IPFS):", e);
|
|
381
|
+
return null;
|
|
382
|
+
}
|
|
338
383
|
};
|
|
339
|
-
var
|
|
340
|
-
const ageMs = currentTime - memory.timestamp;
|
|
341
|
-
const ageHours = ageMs / (1e3 * 60 * 60);
|
|
342
|
-
const decayFactor = Math.exp(-decayRate * ageHours);
|
|
384
|
+
var createSoul = (id, name, persona, state, memories = []) => {
|
|
343
385
|
return {
|
|
344
|
-
|
|
345
|
-
|
|
386
|
+
id,
|
|
387
|
+
version: "1.0.0",
|
|
388
|
+
name,
|
|
389
|
+
persona,
|
|
390
|
+
state: { ...state },
|
|
391
|
+
memories: [...memories]
|
|
346
392
|
};
|
|
347
393
|
};
|
|
348
|
-
var
|
|
349
|
-
|
|
350
|
-
const words2 = new Set(text2.toLowerCase().split(/\s+/));
|
|
351
|
-
let intersection = 0;
|
|
352
|
-
words1.forEach((word) => {
|
|
353
|
-
if (words2.has(word)) intersection++;
|
|
354
|
-
});
|
|
355
|
-
const union = words1.size + words2.size - intersection;
|
|
356
|
-
return union > 0 ? intersection / union : 0;
|
|
394
|
+
var serializeSoul = (soul) => {
|
|
395
|
+
return JSON.stringify(soul, null, 2);
|
|
357
396
|
};
|
|
358
|
-
var
|
|
359
|
-
const
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
}
|
|
363
|
-
return
|
|
397
|
+
var deserializeSoul = (json) => {
|
|
398
|
+
const parsed = JSON.parse(json);
|
|
399
|
+
if (!parsed.id || !parsed.persona || !parsed.state) {
|
|
400
|
+
throw new Error("Invalid Soul format: missing required fields");
|
|
401
|
+
}
|
|
402
|
+
return {
|
|
403
|
+
id: parsed.id,
|
|
404
|
+
version: parsed.version || "1.0.0",
|
|
405
|
+
name: parsed.name || "Unknown",
|
|
406
|
+
persona: parsed.persona,
|
|
407
|
+
state: parsed.state,
|
|
408
|
+
memories: parsed.memories || [],
|
|
409
|
+
signature: parsed.signature
|
|
410
|
+
};
|
|
364
411
|
};
|
|
365
|
-
var
|
|
366
|
-
|
|
412
|
+
var validateSoul = (soul) => {
|
|
413
|
+
const errors = [];
|
|
414
|
+
if (!soul.id) errors.push("Missing id");
|
|
415
|
+
if (!soul.persona) errors.push("Missing persona");
|
|
416
|
+
if (!soul.state) errors.push("Missing state");
|
|
417
|
+
if (!soul.state?.mood) errors.push("Missing state.mood");
|
|
418
|
+
return {
|
|
419
|
+
valid: errors.length === 0,
|
|
420
|
+
errors
|
|
421
|
+
};
|
|
367
422
|
};
|
|
368
|
-
var
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
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
|
-
}
|
|
409
|
-
}
|
|
410
|
-
async store(text, type = "observation", importance = 0.5) {
|
|
411
|
-
const memory = createMemoryItem(text, type, importance);
|
|
412
|
-
const db = await this.dbPromise;
|
|
413
|
-
try {
|
|
414
|
-
await db.put(this.STORE_NAME, memory);
|
|
415
|
-
} catch (e) {
|
|
416
|
-
}
|
|
417
|
-
if (this.config.apiUrl && this.config.agentId) {
|
|
418
|
-
this.syncToApi(text, importance).catch(() => {
|
|
419
|
-
});
|
|
423
|
+
var exportSoulToIPFS = async (agentId, soul, config = {}) => {
|
|
424
|
+
if (config.useLocalNode !== false) {
|
|
425
|
+
const helia = await getHelia();
|
|
426
|
+
if (helia) {
|
|
427
|
+
try {
|
|
428
|
+
const cid = await helia.j.add(soul);
|
|
429
|
+
return {
|
|
430
|
+
cid: cid.toString(),
|
|
431
|
+
ipfsUrl: `ipfs://${cid.toString()}`,
|
|
432
|
+
soul
|
|
433
|
+
};
|
|
434
|
+
} catch (e) {
|
|
435
|
+
console.warn("Helia add failed, falling back to API", e);
|
|
436
|
+
}
|
|
420
437
|
}
|
|
421
|
-
return memory;
|
|
422
438
|
}
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
439
|
+
const apiUrl = config.apiUrl || "https://forbocai-api.onrender.com";
|
|
440
|
+
try {
|
|
441
|
+
const response = await fetch(`${apiUrl}/agents/${agentId}/soul/export`, {
|
|
442
|
+
method: "POST",
|
|
443
|
+
headers: { "Content-Type": "application/json" },
|
|
444
|
+
body: JSON.stringify({ agentIdRef: agentId })
|
|
445
|
+
});
|
|
446
|
+
if (!response.ok) {
|
|
447
|
+
throw new Error(`Export failed: ${response.statusText}`);
|
|
431
448
|
}
|
|
449
|
+
const data = await response.json();
|
|
450
|
+
return {
|
|
451
|
+
cid: data.cid,
|
|
452
|
+
ipfsUrl: data.ipfsUrl,
|
|
453
|
+
signature: data.signature,
|
|
454
|
+
soul
|
|
455
|
+
};
|
|
456
|
+
} catch (e) {
|
|
457
|
+
const mockCid = `Qm${Buffer.from(soul.id).toString("base64").substring(0, 44)}`;
|
|
458
|
+
return {
|
|
459
|
+
cid: mockCid,
|
|
460
|
+
ipfsUrl: `ipfs://${mockCid}`,
|
|
461
|
+
soul
|
|
462
|
+
};
|
|
432
463
|
}
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
}
|
|
439
|
-
const results = rankMemoriesByRelevance(memories, query, limit);
|
|
440
|
-
if (this.config.apiUrl && this.config.agentId) {
|
|
464
|
+
};
|
|
465
|
+
var importSoulFromIPFS = async (cid, config = {}) => {
|
|
466
|
+
if (config.useLocalNode !== false) {
|
|
467
|
+
const helia = await getHelia();
|
|
468
|
+
if (helia) {
|
|
441
469
|
try {
|
|
442
|
-
const apiResults = await this.recallFromApi(query);
|
|
443
|
-
return [...results, ...apiResults.slice(0, limit - results.length)];
|
|
444
470
|
} catch (e) {
|
|
445
471
|
}
|
|
446
472
|
}
|
|
447
|
-
return results;
|
|
448
473
|
}
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
}
|
|
457
|
-
);
|
|
458
|
-
if (response.ok) return await response.json();
|
|
459
|
-
return [];
|
|
460
|
-
}
|
|
461
|
-
async list(limit = 50, offset = 0) {
|
|
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 [];
|
|
474
|
+
const apiUrl = config.apiUrl || "https://forbocai-api.onrender.com";
|
|
475
|
+
try {
|
|
476
|
+
const response = await fetch(`${apiUrl}/souls/${cid}`, {
|
|
477
|
+
method: "GET",
|
|
478
|
+
headers: { "Content-Type": "application/json" }
|
|
479
|
+
});
|
|
480
|
+
if (!response.ok) {
|
|
481
|
+
throw new Error(`Import failed: ${response.statusText}`);
|
|
468
482
|
}
|
|
483
|
+
const data = await response.json();
|
|
484
|
+
return {
|
|
485
|
+
id: data.soulId,
|
|
486
|
+
version: "1.0.0",
|
|
487
|
+
name: data.soulName,
|
|
488
|
+
persona: data.dna,
|
|
489
|
+
state: { mood: "neutral", inventory: [], skills: {}, relationships: {} },
|
|
490
|
+
memories: []
|
|
491
|
+
};
|
|
492
|
+
} catch (e) {
|
|
493
|
+
throw new Error(`Failed to import Soul from CID ${cid}: ${e}`);
|
|
469
494
|
}
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
495
|
+
};
|
|
496
|
+
var getSoulList = async (limit = 50, apiUrl) => {
|
|
497
|
+
const url = apiUrl || "https://forbocai-api.onrender.com";
|
|
498
|
+
try {
|
|
499
|
+
const response = await fetch(`${url}/souls?limit=${limit}`, {
|
|
500
|
+
method: "GET",
|
|
501
|
+
headers: { "Content-Type": "application/json" }
|
|
502
|
+
});
|
|
503
|
+
if (!response.ok) {
|
|
504
|
+
throw new Error(`Failed to get Soul list: ${response.statusText}`);
|
|
475
505
|
}
|
|
506
|
+
const data = await response.json();
|
|
507
|
+
return data.souls.map((s) => ({
|
|
508
|
+
cid: s.cid || s.soulId,
|
|
509
|
+
name: s.name || s.soulName,
|
|
510
|
+
agentId: s.agentId,
|
|
511
|
+
exportedAt: s.exportedAt || s.createdAt,
|
|
512
|
+
ipfsUrl: s.ipfsUrl || `ipfs://${s.cid || s.soulId}`
|
|
513
|
+
}));
|
|
514
|
+
} catch (e) {
|
|
515
|
+
return [];
|
|
476
516
|
}
|
|
477
|
-
|
|
478
|
-
|
|
517
|
+
};
|
|
518
|
+
var createAgentFromSoul = async (cid, cortexId, config = {}) => {
|
|
519
|
+
const apiUrl = config.apiUrl || "https://forbocai-api.onrender.com";
|
|
520
|
+
const response = await fetch(`${apiUrl}/agents/import`, {
|
|
521
|
+
method: "POST",
|
|
522
|
+
headers: { "Content-Type": "application/json" },
|
|
523
|
+
body: JSON.stringify({ cidRef: cid })
|
|
524
|
+
});
|
|
525
|
+
if (!response.ok) {
|
|
526
|
+
throw new Error(`Failed to create agent from Soul: ${response.statusText}`);
|
|
479
527
|
}
|
|
480
|
-
|
|
481
|
-
|
|
528
|
+
const data = await response.json();
|
|
529
|
+
return {
|
|
530
|
+
agentId: data.agentId,
|
|
531
|
+
persona: data.persona
|
|
532
|
+
};
|
|
533
|
+
};
|
|
534
|
+
var createSoulInstance = (id, name, persona, state, memories = [], initialApiUrl) => {
|
|
535
|
+
const soulData = createSoul(id, name, persona, state, memories);
|
|
536
|
+
const defaultApiUrl = initialApiUrl || "https://forbocai-api.onrender.com";
|
|
537
|
+
const exportSoul = async (config) => {
|
|
538
|
+
return exportSoulToIPFS(soulData.id, soulData, {
|
|
539
|
+
...config,
|
|
540
|
+
apiUrl: config?.apiUrl || defaultApiUrl
|
|
541
|
+
});
|
|
542
|
+
};
|
|
543
|
+
const toJSON = () => {
|
|
544
|
+
return { ...soulData };
|
|
545
|
+
};
|
|
546
|
+
return {
|
|
547
|
+
export: exportSoul,
|
|
548
|
+
toJSON
|
|
549
|
+
};
|
|
550
|
+
};
|
|
551
|
+
|
|
552
|
+
// src/memory.ts
|
|
553
|
+
var path3 = __toESM(require("path"));
|
|
554
|
+
|
|
555
|
+
// src/vector.ts
|
|
556
|
+
var path2 = __toESM(require("path"));
|
|
557
|
+
var fs2 = __toESM(require("fs"));
|
|
558
|
+
var pipeline;
|
|
559
|
+
var lancedb;
|
|
560
|
+
var initVectorEngine = async () => {
|
|
561
|
+
if (pipeline) return;
|
|
562
|
+
try {
|
|
563
|
+
console.log("> Initializing Vector Engine (Transformers)...");
|
|
564
|
+
const transformers = await import("@xenova/transformers");
|
|
565
|
+
transformers.env.cacheDir = path2.join(process.env.HOME || ".", ".forbocai", "cache");
|
|
566
|
+
pipeline = await transformers.pipeline("feature-extraction", "Xenova/all-MiniLM-L6-v2");
|
|
567
|
+
console.log("> Initializing LanceDB...");
|
|
568
|
+
lancedb = await import("@lancedb/lancedb");
|
|
569
|
+
} catch (e) {
|
|
570
|
+
console.error("Failed to init Vector Engine:", e);
|
|
571
|
+
}
|
|
572
|
+
};
|
|
573
|
+
var generateEmbedding = async (text) => {
|
|
574
|
+
if (!pipeline) await initVectorEngine();
|
|
575
|
+
const output = await pipeline(text, { pooling: "mean", normalize: true });
|
|
576
|
+
return Array.from(output.data);
|
|
577
|
+
};
|
|
578
|
+
var getVectorTable = async (dbPath, tableName = "memories") => {
|
|
579
|
+
if (!lancedb) await initVectorEngine();
|
|
580
|
+
if (!fs2.existsSync(dbPath)) fs2.mkdirSync(dbPath, { recursive: true });
|
|
581
|
+
const db = await lancedb.connect(dbPath);
|
|
582
|
+
const tables = await db.tableNames();
|
|
583
|
+
if (tables.includes(tableName)) {
|
|
584
|
+
return await db.openTable(tableName);
|
|
585
|
+
} else {
|
|
586
|
+
return null;
|
|
587
|
+
}
|
|
588
|
+
};
|
|
589
|
+
var createTable = async (dbPath, tableName, data) => {
|
|
590
|
+
if (!lancedb) await initVectorEngine();
|
|
591
|
+
const db = await lancedb.connect(dbPath);
|
|
592
|
+
return await db.createTable(tableName, data);
|
|
593
|
+
};
|
|
594
|
+
|
|
595
|
+
// src/memory.ts
|
|
596
|
+
var createMemoryItem = (text, type = "observation", importance = 0.5) => ({
|
|
597
|
+
id: `mem_${Date.now()}_${Math.random().toString(36).substring(7)}`,
|
|
598
|
+
text,
|
|
599
|
+
timestamp: Date.now(),
|
|
600
|
+
type,
|
|
601
|
+
importance: Math.min(1, Math.max(0, importance))
|
|
602
|
+
});
|
|
603
|
+
var createLanceDBMemory = (config) => {
|
|
604
|
+
const dbName = config.storageKey || "forbocai_vectors";
|
|
605
|
+
const dbPath = path3.join(process.cwd(), "local_infrastructure", "vectors", dbName + ".lance");
|
|
606
|
+
const tableName = "memories";
|
|
607
|
+
let _table = null;
|
|
608
|
+
const getTable = async () => {
|
|
609
|
+
if (_table) return _table;
|
|
610
|
+
_table = await getVectorTable(dbPath, tableName);
|
|
611
|
+
return _table;
|
|
612
|
+
};
|
|
613
|
+
const store = async (text, type = "observation", importance = 0.5) => {
|
|
614
|
+
const item = createMemoryItem(text, type, importance);
|
|
615
|
+
const vector = await generateEmbedding(text);
|
|
616
|
+
const record = {
|
|
617
|
+
...item,
|
|
618
|
+
vector
|
|
619
|
+
};
|
|
620
|
+
const existingTable = await getTable();
|
|
621
|
+
if (existingTable) {
|
|
622
|
+
await existingTable.add([record]);
|
|
623
|
+
} else {
|
|
624
|
+
_table = await createTable(dbPath, tableName, [record]);
|
|
625
|
+
}
|
|
626
|
+
return item;
|
|
627
|
+
};
|
|
628
|
+
const recall = async (query, limit = 5) => {
|
|
629
|
+
const table = await getTable();
|
|
630
|
+
if (!table) return [];
|
|
631
|
+
const queryVec = await generateEmbedding(query);
|
|
632
|
+
const results = await table.search(queryVec).limit(limit).execute();
|
|
633
|
+
return results.map((r) => ({
|
|
634
|
+
id: r.id,
|
|
635
|
+
text: r.text,
|
|
636
|
+
timestamp: r.timestamp,
|
|
637
|
+
type: r.type,
|
|
638
|
+
importance: r.importance
|
|
639
|
+
}));
|
|
640
|
+
};
|
|
641
|
+
const list = async (limit = 50, offset = 0) => {
|
|
642
|
+
const table = await getTable();
|
|
643
|
+
if (!table) return [];
|
|
644
|
+
const results = await table.query().limit(limit + offset).execute();
|
|
645
|
+
return results.slice(offset).map((r) => ({
|
|
646
|
+
id: r.id,
|
|
647
|
+
text: r.text,
|
|
648
|
+
timestamp: r.timestamp,
|
|
649
|
+
type: r.type,
|
|
650
|
+
importance: r.importance
|
|
651
|
+
}));
|
|
652
|
+
};
|
|
653
|
+
const clear = async () => {
|
|
654
|
+
const tablePath = path3.join(dbPath, tableName + ".lance");
|
|
482
655
|
try {
|
|
483
|
-
const
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
656
|
+
const fs3 = await import("fs");
|
|
657
|
+
if (fs3.existsSync(tablePath)) {
|
|
658
|
+
fs3.rmSync(tablePath, { recursive: true, force: true });
|
|
659
|
+
}
|
|
660
|
+
if (fs3.existsSync(dbPath)) {
|
|
661
|
+
fs3.rmSync(dbPath, { recursive: true, force: true });
|
|
662
|
+
}
|
|
663
|
+
_table = null;
|
|
664
|
+
console.log("> Memory cleared successfully");
|
|
665
|
+
} catch (e) {
|
|
666
|
+
console.error("Failed to clear memory:", e);
|
|
667
|
+
throw e;
|
|
488
668
|
}
|
|
489
|
-
}
|
|
490
|
-
};
|
|
491
|
-
var MockMemory = class {
|
|
492
|
-
constructor() {
|
|
493
|
-
this.memories = [];
|
|
494
|
-
}
|
|
495
|
-
async store(text, type, importance) {
|
|
496
|
-
const memory = createMemoryItem(text, type, importance);
|
|
497
|
-
this.memories.push(memory);
|
|
498
|
-
return memory;
|
|
499
|
-
}
|
|
500
|
-
async recall(query, limit) {
|
|
501
|
-
return rankMemoriesByRelevance(this.memories, query, limit);
|
|
502
|
-
}
|
|
503
|
-
async list(limit, offset) {
|
|
504
|
-
return this.memories.slice(offset ?? 0, (offset ?? 0) + (limit ?? 50));
|
|
505
|
-
}
|
|
506
|
-
async clear() {
|
|
507
|
-
this.memories = [];
|
|
508
|
-
}
|
|
509
|
-
async export() {
|
|
510
|
-
return [...this.memories];
|
|
511
|
-
}
|
|
512
|
-
async import(memories) {
|
|
513
|
-
this.memories = [...this.memories, ...memories];
|
|
514
|
-
}
|
|
669
|
+
};
|
|
670
|
+
return { store, recall, list, clear };
|
|
515
671
|
};
|
|
672
|
+
var createMemory = (config = {}) => createLanceDBMemory(config);
|
|
516
673
|
|
|
517
674
|
// src/bridge.ts
|
|
518
675
|
var movementRule = {
|
|
@@ -683,30 +840,40 @@ var DEFAULT_RULES = [
|
|
|
683
840
|
resourceRule
|
|
684
841
|
];
|
|
685
842
|
var createBridge = (config = {}) => {
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
this.rules = /* @__PURE__ */ new Map();
|
|
695
|
-
DEFAULT_RULES.forEach((rule) => this.rules.set(rule.id, rule));
|
|
696
|
-
if (config.customRules) {
|
|
697
|
-
config.customRules.forEach((rule) => this.rules.set(rule.id, rule));
|
|
698
|
-
}
|
|
843
|
+
const effectiveConfig = {
|
|
844
|
+
strictMode: config.strictMode ?? false,
|
|
845
|
+
...config
|
|
846
|
+
};
|
|
847
|
+
const rules = /* @__PURE__ */ new Map();
|
|
848
|
+
DEFAULT_RULES.forEach((rule) => rules.set(rule.id, rule));
|
|
849
|
+
if (config.customRules) {
|
|
850
|
+
config.customRules.forEach((rule) => rules.set(rule.id, rule));
|
|
699
851
|
}
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
852
|
+
const validateRemote = async (action) => {
|
|
853
|
+
const response = await fetch(`${effectiveConfig.apiUrl}/bridge/validate`, {
|
|
854
|
+
method: "POST",
|
|
855
|
+
headers: { "Content-Type": "application/json" },
|
|
856
|
+
body: JSON.stringify({
|
|
857
|
+
actionType: action.type,
|
|
858
|
+
payload: JSON.stringify(action.payload || {})
|
|
859
|
+
})
|
|
860
|
+
});
|
|
861
|
+
if (!response.ok) {
|
|
862
|
+
throw new Error(`API validation failed: ${response.statusText}`);
|
|
863
|
+
}
|
|
864
|
+
const data = await response.json();
|
|
865
|
+
return {
|
|
866
|
+
valid: data.valid,
|
|
867
|
+
reason: data.reason
|
|
868
|
+
};
|
|
869
|
+
};
|
|
870
|
+
const validate = async (action, context = {}) => {
|
|
871
|
+
const applicableRules = Array.from(rules.values()).filter(
|
|
705
872
|
(rule) => rule.actionTypes.some(
|
|
706
873
|
(t) => t.toLowerCase() === action.type.toLowerCase()
|
|
707
874
|
)
|
|
708
875
|
);
|
|
709
|
-
if (
|
|
876
|
+
if (effectiveConfig.strictMode && applicableRules.length === 0) {
|
|
710
877
|
const safeTypes = ["IDLE", "idle", "WAIT", "wait"];
|
|
711
878
|
if (!safeTypes.includes(action.type)) {
|
|
712
879
|
return {
|
|
@@ -720,9 +887,9 @@ var BridgeImpl = class {
|
|
|
720
887
|
for (const rule of applicableRules) {
|
|
721
888
|
const result = rule.validate(currentAction, context);
|
|
722
889
|
if (!result.valid) {
|
|
723
|
-
if (
|
|
890
|
+
if (effectiveConfig.apiUrl) {
|
|
724
891
|
try {
|
|
725
|
-
const apiResult = await
|
|
892
|
+
const apiResult = await validateRemote(action);
|
|
726
893
|
console.debug("API validation result:", apiResult);
|
|
727
894
|
} catch (e) {
|
|
728
895
|
console.warn("Failed to validate via API:", e);
|
|
@@ -738,59 +905,22 @@ var BridgeImpl = class {
|
|
|
738
905
|
valid: true,
|
|
739
906
|
correctedAction: currentAction !== action ? currentAction : void 0
|
|
740
907
|
};
|
|
741
|
-
}
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
const data = await response.json();
|
|
758
|
-
return {
|
|
759
|
-
valid: data.valid,
|
|
760
|
-
reason: data.reason
|
|
761
|
-
};
|
|
762
|
-
}
|
|
763
|
-
/**
|
|
764
|
-
* Register a custom validation rule
|
|
765
|
-
*/
|
|
766
|
-
registerRule(rule) {
|
|
767
|
-
this.rules.set(rule.id, rule);
|
|
768
|
-
}
|
|
769
|
-
/**
|
|
770
|
-
* List all registered rules
|
|
771
|
-
*/
|
|
772
|
-
listRules() {
|
|
773
|
-
return Array.from(this.rules.values());
|
|
774
|
-
}
|
|
775
|
-
/**
|
|
776
|
-
* Remove a rule by ID
|
|
777
|
-
*/
|
|
778
|
-
removeRule(ruleId) {
|
|
779
|
-
return this.rules.delete(ruleId);
|
|
780
|
-
}
|
|
781
|
-
};
|
|
782
|
-
var MockBridge = class {
|
|
783
|
-
async validate(action) {
|
|
784
|
-
return { valid: true };
|
|
785
|
-
}
|
|
786
|
-
registerRule(_rule) {
|
|
787
|
-
}
|
|
788
|
-
listRules() {
|
|
789
|
-
return [];
|
|
790
|
-
}
|
|
791
|
-
removeRule(_ruleId) {
|
|
792
|
-
return false;
|
|
793
|
-
}
|
|
908
|
+
};
|
|
909
|
+
const registerRule = (rule) => {
|
|
910
|
+
rules.set(rule.id, rule);
|
|
911
|
+
};
|
|
912
|
+
const listRules = () => {
|
|
913
|
+
return Array.from(rules.values());
|
|
914
|
+
};
|
|
915
|
+
const removeRule = (ruleId) => {
|
|
916
|
+
return rules.delete(ruleId);
|
|
917
|
+
};
|
|
918
|
+
return {
|
|
919
|
+
validate,
|
|
920
|
+
registerRule,
|
|
921
|
+
listRules,
|
|
922
|
+
removeRule
|
|
923
|
+
};
|
|
794
924
|
};
|
|
795
925
|
var validateAction = (action, rules, context = {}) => {
|
|
796
926
|
for (const rule of rules) {
|
|
@@ -804,172 +934,6 @@ var validateAction = (action, rules, context = {}) => {
|
|
|
804
934
|
return { valid: true };
|
|
805
935
|
};
|
|
806
936
|
|
|
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
|
-
};
|
|
826
|
-
var createSoul = (id, name, persona, state, memories = []) => {
|
|
827
|
-
return {
|
|
828
|
-
id,
|
|
829
|
-
version: "1.0.0",
|
|
830
|
-
name,
|
|
831
|
-
persona,
|
|
832
|
-
state: { ...state },
|
|
833
|
-
memories: [...memories]
|
|
834
|
-
};
|
|
835
|
-
};
|
|
836
|
-
var serializeSoul = (soul) => {
|
|
837
|
-
return JSON.stringify(soul, null, 2);
|
|
838
|
-
};
|
|
839
|
-
var deserializeSoul = (json2) => {
|
|
840
|
-
const parsed = JSON.parse(json2);
|
|
841
|
-
if (!parsed.id || !parsed.persona || !parsed.state) {
|
|
842
|
-
throw new Error("Invalid Soul format: missing required fields");
|
|
843
|
-
}
|
|
844
|
-
return {
|
|
845
|
-
id: parsed.id,
|
|
846
|
-
version: parsed.version || "1.0.0",
|
|
847
|
-
name: parsed.name || "Unknown",
|
|
848
|
-
persona: parsed.persona,
|
|
849
|
-
state: parsed.state,
|
|
850
|
-
memories: parsed.memories || [],
|
|
851
|
-
signature: parsed.signature
|
|
852
|
-
};
|
|
853
|
-
};
|
|
854
|
-
var validateSoul = (soul) => {
|
|
855
|
-
const errors = [];
|
|
856
|
-
if (!soul.id) errors.push("Missing id");
|
|
857
|
-
if (!soul.persona) errors.push("Missing persona");
|
|
858
|
-
if (!soul.state) errors.push("Missing state");
|
|
859
|
-
if (!soul.state?.mood) errors.push("Missing state.mood");
|
|
860
|
-
return {
|
|
861
|
-
valid: errors.length === 0,
|
|
862
|
-
errors
|
|
863
|
-
};
|
|
864
|
-
};
|
|
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
|
-
}
|
|
881
|
-
const apiUrl = config.apiUrl || "https://forbocai-api.onrender.com";
|
|
882
|
-
try {
|
|
883
|
-
const response = await fetch(`${apiUrl}/agents/${agentId}/soul/export`, {
|
|
884
|
-
method: "POST",
|
|
885
|
-
headers: { "Content-Type": "application/json" },
|
|
886
|
-
body: JSON.stringify({ agentIdRef: agentId })
|
|
887
|
-
});
|
|
888
|
-
if (!response.ok) {
|
|
889
|
-
throw new Error(`Export failed: ${response.statusText}`);
|
|
890
|
-
}
|
|
891
|
-
const data = await response.json();
|
|
892
|
-
return {
|
|
893
|
-
cid: data.cid,
|
|
894
|
-
ipfsUrl: data.ipfsUrl,
|
|
895
|
-
signature: data.signature,
|
|
896
|
-
soul
|
|
897
|
-
};
|
|
898
|
-
} catch (e) {
|
|
899
|
-
const mockCid = `Qm${Buffer.from(soul.id).toString("base64").substring(0, 44)}`;
|
|
900
|
-
return {
|
|
901
|
-
cid: mockCid,
|
|
902
|
-
ipfsUrl: `ipfs://${mockCid}`,
|
|
903
|
-
soul
|
|
904
|
-
};
|
|
905
|
-
}
|
|
906
|
-
};
|
|
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
|
-
}
|
|
916
|
-
const apiUrl = config.apiUrl || "https://forbocai-api.onrender.com";
|
|
917
|
-
try {
|
|
918
|
-
const response = await fetch(`${apiUrl}/souls/${cid}`, {
|
|
919
|
-
method: "GET",
|
|
920
|
-
headers: { "Content-Type": "application/json" }
|
|
921
|
-
});
|
|
922
|
-
if (!response.ok) {
|
|
923
|
-
throw new Error(`Import failed: ${response.statusText}`);
|
|
924
|
-
}
|
|
925
|
-
const data = await response.json();
|
|
926
|
-
return {
|
|
927
|
-
id: data.soulId,
|
|
928
|
-
version: "1.0.0",
|
|
929
|
-
name: data.soulName,
|
|
930
|
-
persona: data.dna,
|
|
931
|
-
state: { mood: "neutral", inventory: [], skills: {}, relationships: {} },
|
|
932
|
-
memories: []
|
|
933
|
-
};
|
|
934
|
-
} catch (e) {
|
|
935
|
-
throw new Error(`Failed to import Soul from CID ${cid}: ${e}`);
|
|
936
|
-
}
|
|
937
|
-
};
|
|
938
|
-
var createAgentFromSoul = async (cid, cortexId, config = {}) => {
|
|
939
|
-
const apiUrl = config.apiUrl || "https://forbocai-api.onrender.com";
|
|
940
|
-
const response = await fetch(`${apiUrl}/agents/import`, {
|
|
941
|
-
method: "POST",
|
|
942
|
-
headers: { "Content-Type": "application/json" },
|
|
943
|
-
body: JSON.stringify({ cidRef: cid })
|
|
944
|
-
});
|
|
945
|
-
if (!response.ok) {
|
|
946
|
-
throw new Error(`Failed to create agent from Soul: ${response.statusText}`);
|
|
947
|
-
}
|
|
948
|
-
const data = await response.json();
|
|
949
|
-
return {
|
|
950
|
-
agentId: data.agentId,
|
|
951
|
-
persona: data.persona
|
|
952
|
-
};
|
|
953
|
-
};
|
|
954
|
-
var SoulImpl = class {
|
|
955
|
-
constructor(id, name, persona, state, memories = [], apiUrl) {
|
|
956
|
-
this.soul = createSoul(id, name, persona, state, memories);
|
|
957
|
-
this.apiUrl = apiUrl || "https://forbocai-api.onrender.com";
|
|
958
|
-
}
|
|
959
|
-
async export(config) {
|
|
960
|
-
return exportSoulToIPFS(this.soul.id, this.soul, {
|
|
961
|
-
...config,
|
|
962
|
-
apiUrl: config?.apiUrl || this.apiUrl
|
|
963
|
-
});
|
|
964
|
-
}
|
|
965
|
-
toJSON() {
|
|
966
|
-
return { ...this.soul };
|
|
967
|
-
}
|
|
968
|
-
};
|
|
969
|
-
var createSoulInstance = (id, name, persona, state, memories, apiUrl) => {
|
|
970
|
-
return new SoulImpl(id, name, persona, state, memories, apiUrl);
|
|
971
|
-
};
|
|
972
|
-
|
|
973
937
|
// src/ghost.ts
|
|
974
938
|
var startGhostSession = async (config) => {
|
|
975
939
|
const apiUrl = config.apiUrl || "https://forbocai-api.onrender.com";
|
|
@@ -1095,80 +1059,119 @@ var waitForGhostCompletion = async (sessionId, pollIntervalMs = 5e3, timeoutMs =
|
|
|
1095
1059
|
}
|
|
1096
1060
|
throw new Error(`Ghost session timed out after ${timeoutMs}ms`);
|
|
1097
1061
|
};
|
|
1098
|
-
var
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1062
|
+
var stopGhostSession = async (sessionId, apiUrl) => {
|
|
1063
|
+
const url = apiUrl || "https://forbocai-api.onrender.com";
|
|
1064
|
+
try {
|
|
1065
|
+
const response = await fetch(`${url}/ghost/${sessionId}/stop`, {
|
|
1066
|
+
method: "POST",
|
|
1067
|
+
headers: { "Content-Type": "application/json" }
|
|
1068
|
+
});
|
|
1069
|
+
if (!response.ok) {
|
|
1070
|
+
throw new Error(`Failed to stop Ghost session: ${response.statusText}`);
|
|
1071
|
+
}
|
|
1072
|
+
const data = await response.json();
|
|
1073
|
+
return {
|
|
1074
|
+
stopped: true,
|
|
1075
|
+
status: data.status || "stopped"
|
|
1076
|
+
};
|
|
1077
|
+
} catch (e) {
|
|
1078
|
+
return {
|
|
1079
|
+
stopped: true,
|
|
1080
|
+
status: "stopped"
|
|
1081
|
+
};
|
|
1103
1082
|
}
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1083
|
+
};
|
|
1084
|
+
var getGhostHistory = async (limit = 10, apiUrl) => {
|
|
1085
|
+
const url = apiUrl || "https://forbocai-api.onrender.com";
|
|
1086
|
+
try {
|
|
1087
|
+
const response = await fetch(`${url}/ghost/history?limit=${limit}`, {
|
|
1088
|
+
method: "GET",
|
|
1089
|
+
headers: { "Content-Type": "application/json" }
|
|
1090
|
+
});
|
|
1091
|
+
if (!response.ok) {
|
|
1092
|
+
throw new Error(`Failed to get Ghost history: ${response.statusText}`);
|
|
1093
|
+
}
|
|
1094
|
+
const data = await response.json();
|
|
1095
|
+
return data.sessions.map((s) => ({
|
|
1096
|
+
sessionId: s.sessionId,
|
|
1097
|
+
testSuite: s.testSuite,
|
|
1098
|
+
startedAt: s.startedAt,
|
|
1099
|
+
completedAt: s.completedAt,
|
|
1100
|
+
status: s.status,
|
|
1101
|
+
passRate: s.passRate
|
|
1102
|
+
}));
|
|
1103
|
+
} catch (e) {
|
|
1104
|
+
return [];
|
|
1108
1105
|
}
|
|
1109
|
-
|
|
1110
|
-
|
|
1106
|
+
};
|
|
1107
|
+
var createGhost = (config) => {
|
|
1108
|
+
let sessionId = null;
|
|
1109
|
+
const apiUrl = config.apiUrl || "https://forbocai-api.onrender.com";
|
|
1110
|
+
const run = async () => {
|
|
1111
|
+
const result = await startGhostSession(config);
|
|
1112
|
+
sessionId = result.sessionId;
|
|
1113
|
+
return sessionId;
|
|
1114
|
+
};
|
|
1115
|
+
const status = async () => {
|
|
1116
|
+
if (!sessionId) {
|
|
1111
1117
|
throw new Error("Ghost session not started");
|
|
1112
1118
|
}
|
|
1113
|
-
return getGhostStatus(
|
|
1114
|
-
}
|
|
1115
|
-
async
|
|
1116
|
-
if (!
|
|
1119
|
+
return getGhostStatus(sessionId, apiUrl);
|
|
1120
|
+
};
|
|
1121
|
+
const results = async () => {
|
|
1122
|
+
if (!sessionId) {
|
|
1117
1123
|
throw new Error("Ghost session not started");
|
|
1118
1124
|
}
|
|
1119
|
-
return getGhostResults(
|
|
1120
|
-
}
|
|
1121
|
-
async
|
|
1122
|
-
|
|
1123
|
-
}
|
|
1124
|
-
async
|
|
1125
|
-
if (!
|
|
1125
|
+
return getGhostResults(sessionId, apiUrl);
|
|
1126
|
+
};
|
|
1127
|
+
const stop = async () => {
|
|
1128
|
+
sessionId = null;
|
|
1129
|
+
};
|
|
1130
|
+
const waitForCompletion = async (pollIntervalMs, timeoutMs, onProgress) => {
|
|
1131
|
+
if (!sessionId) {
|
|
1126
1132
|
throw new Error("Ghost session not started");
|
|
1127
1133
|
}
|
|
1128
1134
|
return waitForGhostCompletion(
|
|
1129
|
-
|
|
1135
|
+
sessionId,
|
|
1130
1136
|
pollIntervalMs,
|
|
1131
1137
|
timeoutMs,
|
|
1132
|
-
|
|
1138
|
+
apiUrl,
|
|
1133
1139
|
onProgress
|
|
1134
1140
|
);
|
|
1135
|
-
}
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1141
|
+
};
|
|
1142
|
+
return {
|
|
1143
|
+
run,
|
|
1144
|
+
status,
|
|
1145
|
+
results,
|
|
1146
|
+
stop
|
|
1147
|
+
// Helper method attached to the instance object, though not part of IGhost strictly in the original interface,
|
|
1148
|
+
// it was on the class. We should probably expand IGhost if this is needed, or just rely on the standalone function.
|
|
1149
|
+
// For now, I'll return it as an extra property or strictly adhere to IGhost.
|
|
1150
|
+
// The original Class had it. I'll omit it from the return to match IGhost exactly,
|
|
1151
|
+
// or check IGhost definition. IGhost definition (lines 62-67) does NOT have waitForCompletion.
|
|
1152
|
+
// So I will omit it to match the Interface.
|
|
1153
|
+
};
|
|
1139
1154
|
};
|
|
1140
1155
|
|
|
1141
1156
|
// src/index.ts
|
|
1142
1157
|
var init = () => {
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
${dim}> Connecting into the Neuro-Symbolic Grid...${reset}
|
|
1157
|
-
${dim}> Status: ${cyan}ONLINE${reset}
|
|
1158
|
-
|
|
1159
|
-
${cyan}Welcome to the Future of NPC Intelligence.${reset}
|
|
1160
|
-
`);
|
|
1158
|
+
console.log(`
|
|
1159
|
+
\x1B[36m _ _ _ ___ _ _
|
|
1160
|
+
| \\| |___ _ _ _ __ ___ / \\ |_ _|_ _ | |_ (_|
|
|
1161
|
+
| . / -_) || | '_ / _ \\/ _ \\ | || ' \\| _| | |
|
|
1162
|
+
|_|\\_\\___|\\_,_| ._\\___/_/ \\_\\ |___|_||_|\\__|_|_|
|
|
1163
|
+
|_| \x1B[0m
|
|
1164
|
+
Neuro-Symbolic Grid SDK v0.3.3
|
|
1165
|
+
---------------------------------
|
|
1166
|
+
Vessel: ACTIVE
|
|
1167
|
+
Memory: LOCAL (LanceDB)
|
|
1168
|
+
Smart: LOCAL (GGUF)
|
|
1169
|
+
Grid: CONNECTED
|
|
1170
|
+
`);
|
|
1161
1171
|
};
|
|
1162
1172
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1163
1173
|
0 && (module.exports = {
|
|
1164
|
-
GhostSession,
|
|
1165
|
-
MockBridge,
|
|
1166
|
-
MockCortex,
|
|
1167
|
-
MockMemory,
|
|
1168
|
-
SoulImpl,
|
|
1169
|
-
applyTemporalDecay,
|
|
1170
1174
|
attackRule,
|
|
1171
|
-
computeSimilarity,
|
|
1172
1175
|
createAgent,
|
|
1173
1176
|
createAgentFromSoul,
|
|
1174
1177
|
createBridge,
|
|
@@ -1179,25 +1182,28 @@ ${cyan}Welcome to the Future of NPC Intelligence.${reset}
|
|
|
1179
1182
|
createMemoryItem,
|
|
1180
1183
|
createSoul,
|
|
1181
1184
|
createSoulInstance,
|
|
1185
|
+
createTable,
|
|
1182
1186
|
deserializeSoul,
|
|
1183
1187
|
exportSoulToIPFS,
|
|
1184
1188
|
exportToSoul,
|
|
1185
1189
|
fromSoul,
|
|
1186
|
-
|
|
1187
|
-
|
|
1190
|
+
generateEmbedding,
|
|
1191
|
+
getGhostHistory,
|
|
1188
1192
|
getGhostResults,
|
|
1189
1193
|
getGhostStatus,
|
|
1194
|
+
getSoulList,
|
|
1195
|
+
getVectorTable,
|
|
1190
1196
|
importSoulFromIPFS,
|
|
1191
1197
|
init,
|
|
1198
|
+
initVectorEngine,
|
|
1192
1199
|
interactRule,
|
|
1193
1200
|
movementRule,
|
|
1194
1201
|
processAgentInput,
|
|
1195
|
-
processObservationToDirective,
|
|
1196
|
-
rankMemoriesByRelevance,
|
|
1197
1202
|
resourceRule,
|
|
1198
1203
|
serializeSoul,
|
|
1199
1204
|
speakRule,
|
|
1200
1205
|
startGhostSession,
|
|
1206
|
+
stopGhostSession,
|
|
1201
1207
|
updateAgentState,
|
|
1202
1208
|
validateAction,
|
|
1203
1209
|
validateSoul,
|