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.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,126 +71,181 @@ __export(index_exports, {
|
|
|
65
71
|
module.exports = __toCommonJS(index_exports);
|
|
66
72
|
|
|
67
73
|
// src/cortex.ts
|
|
68
|
-
var
|
|
69
|
-
|
|
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"
|
|
70
80
|
};
|
|
71
|
-
var
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
81
|
+
var DEFAULT_MODEL = "smollm2-135m";
|
|
82
|
+
var ensureDirectoryExists = (dirPath) => {
|
|
83
|
+
if (!fs.existsSync(dirPath)) {
|
|
84
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
85
|
+
}
|
|
76
86
|
};
|
|
77
|
-
var
|
|
78
|
-
return {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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
|
+
});
|
|
83
110
|
};
|
|
84
|
-
var
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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"
|
|
88
117
|
};
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
maxTokens: config.maxTokens ?? 1024,
|
|
97
|
-
gpu: config.gpu ?? true
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
async init() {
|
|
101
|
-
if (this.status?.ready) {
|
|
102
|
-
return this.status;
|
|
103
|
-
}
|
|
104
|
-
const apiUrl = this.config.apiUrl || "http://localhost:8080";
|
|
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;
|
|
105
125
|
try {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
})
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
console.
|
|
117
|
-
|
|
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.`);
|
|
137
|
+
} else {
|
|
138
|
+
console.log(`> Using cached model: ${modelPath}`);
|
|
118
139
|
}
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
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,
|
|
123
156
|
ready: true,
|
|
124
|
-
engine: "
|
|
157
|
+
engine: "node-llama-cpp"
|
|
125
158
|
};
|
|
126
|
-
|
|
127
|
-
return
|
|
159
|
+
console.log("> Cortex Ready.");
|
|
160
|
+
return status;
|
|
128
161
|
} catch (e) {
|
|
129
|
-
console.
|
|
130
|
-
|
|
131
|
-
id: "offline-" + Math.random().toString(36).substring(7),
|
|
132
|
-
model: this.config.model,
|
|
133
|
-
ready: true,
|
|
134
|
-
engine: "mock"
|
|
135
|
-
};
|
|
136
|
-
this.status = newStatus;
|
|
137
|
-
return newStatus;
|
|
162
|
+
console.error("Failed to initialize Native Cortex:", e);
|
|
163
|
+
throw e;
|
|
138
164
|
}
|
|
139
|
-
}
|
|
140
|
-
async
|
|
141
|
-
if (!
|
|
142
|
-
|
|
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
|
+
}
|
|
194
|
+
}
|
|
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
|
+
});
|
|
206
|
+
}
|
|
143
207
|
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
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 [];
|
|
151
216
|
}
|
|
152
|
-
}
|
|
153
|
-
async
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
return {
|
|
183
|
-
type: "mock-action",
|
|
184
|
-
reason: "Mock action generation"
|
|
185
|
-
};
|
|
186
|
-
}
|
|
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
|
+
};
|
|
187
247
|
};
|
|
248
|
+
var createCortex = (config) => createNativeCortex(config);
|
|
188
249
|
|
|
189
250
|
// src/agent.ts
|
|
190
251
|
var createInitialState = (partial) => {
|
|
@@ -227,19 +288,19 @@ var exportToSoul = (agentId, name, persona, state, memories) => {
|
|
|
227
288
|
};
|
|
228
289
|
};
|
|
229
290
|
var createAgent = (config) => {
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
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();
|
|
241
303
|
const hp = currentState.hp || 100;
|
|
242
|
-
const apiUrl = this.config.apiUrl || "http://localhost:8080";
|
|
243
304
|
const apiContext = Object.entries(context).map(([k, v]) => [k, String(v)]);
|
|
244
305
|
apiContext.push(["hp", String(hp)]);
|
|
245
306
|
let directive = "Respond normally.";
|
|
@@ -267,227 +328,348 @@ var AgentImpl = class {
|
|
|
267
328
|
|
|
268
329
|
User: ${input}
|
|
269
330
|
Agent:`;
|
|
270
|
-
const generatedText = await
|
|
331
|
+
const generatedText = await cortex.complete(prompt);
|
|
271
332
|
return {
|
|
272
333
|
dialogue: generatedText,
|
|
273
334
|
action: { type: instruction, reason: directive },
|
|
274
335
|
thought: `Directive: ${directive}`
|
|
275
336
|
};
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
return { ...this.state };
|
|
279
|
-
}
|
|
280
|
-
setState(newState) {
|
|
281
|
-
this.state = newState;
|
|
282
|
-
}
|
|
283
|
-
export() {
|
|
337
|
+
};
|
|
338
|
+
const exportSoul = () => {
|
|
284
339
|
return exportToSoul(
|
|
285
340
|
"agent-" + Math.random().toString(36).substring(7),
|
|
286
341
|
"Agent",
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
342
|
+
config.persona,
|
|
343
|
+
state,
|
|
344
|
+
memories
|
|
290
345
|
);
|
|
291
|
-
}
|
|
346
|
+
};
|
|
347
|
+
return {
|
|
348
|
+
process: process2,
|
|
349
|
+
getState: getAgentState,
|
|
350
|
+
setState: setAgentState,
|
|
351
|
+
export: exportSoul
|
|
352
|
+
};
|
|
292
353
|
};
|
|
293
|
-
var fromSoul = (soul, cortex) => {
|
|
354
|
+
var fromSoul = (soul, cortex, memory) => {
|
|
294
355
|
const agent = createAgent({
|
|
356
|
+
id: soul.id,
|
|
295
357
|
cortex,
|
|
358
|
+
memory: memory || null,
|
|
296
359
|
persona: soul.persona,
|
|
297
360
|
initialState: soul.state
|
|
298
361
|
});
|
|
299
362
|
return agent;
|
|
300
363
|
};
|
|
301
364
|
|
|
302
|
-
// src/
|
|
303
|
-
var
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
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
|
+
}
|
|
312
383
|
};
|
|
313
|
-
var
|
|
314
|
-
const ageMs = currentTime - memory.timestamp;
|
|
315
|
-
const ageHours = ageMs / (1e3 * 60 * 60);
|
|
316
|
-
const decayFactor = Math.exp(-decayRate * ageHours);
|
|
384
|
+
var createSoul = (id, name, persona, state, memories = []) => {
|
|
317
385
|
return {
|
|
318
|
-
|
|
319
|
-
|
|
386
|
+
id,
|
|
387
|
+
version: "1.0.0",
|
|
388
|
+
name,
|
|
389
|
+
persona,
|
|
390
|
+
state: { ...state },
|
|
391
|
+
memories: [...memories]
|
|
320
392
|
};
|
|
321
393
|
};
|
|
322
|
-
var
|
|
323
|
-
|
|
324
|
-
const words2 = new Set(text2.toLowerCase().split(/\s+/));
|
|
325
|
-
let intersection = 0;
|
|
326
|
-
words1.forEach((word) => {
|
|
327
|
-
if (words2.has(word)) intersection++;
|
|
328
|
-
});
|
|
329
|
-
const union = words1.size + words2.size - intersection;
|
|
330
|
-
return union > 0 ? intersection / union : 0;
|
|
394
|
+
var serializeSoul = (soul) => {
|
|
395
|
+
return JSON.stringify(soul, null, 2);
|
|
331
396
|
};
|
|
332
|
-
var
|
|
333
|
-
const
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
}
|
|
337
|
-
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
|
+
};
|
|
338
411
|
};
|
|
339
|
-
var
|
|
340
|
-
|
|
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
|
+
};
|
|
341
422
|
};
|
|
342
|
-
var
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
maxContextWindow: config.maxContextWindow ?? 10,
|
|
347
|
-
storageKey: config.storageKey ?? "forbocai_memory",
|
|
348
|
-
...config
|
|
349
|
-
};
|
|
350
|
-
this.memories = [];
|
|
351
|
-
this.loadFromStorage();
|
|
352
|
-
}
|
|
353
|
-
/**
|
|
354
|
-
* Store a new memory observation
|
|
355
|
-
*/
|
|
356
|
-
async store(text, type = "observation", importance = 0.5) {
|
|
357
|
-
const memory = createMemoryItem(text, type, importance);
|
|
358
|
-
this.memories = [...this.memories, memory];
|
|
359
|
-
this.saveToStorage();
|
|
360
|
-
if (this.config.apiUrl && this.config.agentId) {
|
|
423
|
+
var exportSoulToIPFS = async (agentId, soul, config = {}) => {
|
|
424
|
+
if (config.useLocalNode !== false) {
|
|
425
|
+
const helia = await getHelia();
|
|
426
|
+
if (helia) {
|
|
361
427
|
try {
|
|
362
|
-
await
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
})
|
|
369
|
-
});
|
|
428
|
+
const cid = await helia.j.add(soul);
|
|
429
|
+
return {
|
|
430
|
+
cid: cid.toString(),
|
|
431
|
+
ipfsUrl: `ipfs://${cid.toString()}`,
|
|
432
|
+
soul
|
|
433
|
+
};
|
|
370
434
|
} catch (e) {
|
|
371
|
-
console.warn("
|
|
435
|
+
console.warn("Helia add failed, falling back to API", e);
|
|
372
436
|
}
|
|
373
437
|
}
|
|
374
|
-
return memory;
|
|
375
438
|
}
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
);
|
|
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}`);
|
|
386
448
|
}
|
|
387
|
-
const
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
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
|
+
};
|
|
463
|
+
}
|
|
464
|
+
};
|
|
465
|
+
var importSoulFromIPFS = async (cid, config = {}) => {
|
|
466
|
+
if (config.useLocalNode !== false) {
|
|
467
|
+
const helia = await getHelia();
|
|
468
|
+
if (helia) {
|
|
393
469
|
try {
|
|
394
|
-
const response = await fetch(
|
|
395
|
-
`${this.config.apiUrl}/agents/${this.config.agentId}/memory/recall`,
|
|
396
|
-
{
|
|
397
|
-
method: "POST",
|
|
398
|
-
headers: { "Content-Type": "application/json" },
|
|
399
|
-
body: JSON.stringify({ query, similarity: 0.5 })
|
|
400
|
-
}
|
|
401
|
-
);
|
|
402
|
-
if (response.ok) {
|
|
403
|
-
const apiMemories = await response.json();
|
|
404
|
-
return [...results, ...apiMemories.slice(0, limit - results.length)];
|
|
405
|
-
}
|
|
406
470
|
} catch (e) {
|
|
407
|
-
console.warn("Failed to recall from API:", e);
|
|
408
471
|
}
|
|
409
472
|
}
|
|
410
|
-
return results;
|
|
411
|
-
}
|
|
412
|
-
/**
|
|
413
|
-
* List all memories (paginated)
|
|
414
|
-
*/
|
|
415
|
-
async list(limit = 50, offset = 0) {
|
|
416
|
-
return this.memories.slice(offset, offset + limit).map((m) => ({ ...m }));
|
|
417
|
-
}
|
|
418
|
-
/**
|
|
419
|
-
* Clear all memories
|
|
420
|
-
*/
|
|
421
|
-
async clear() {
|
|
422
|
-
this.memories = [];
|
|
423
|
-
this.saveToStorage();
|
|
424
|
-
}
|
|
425
|
-
/**
|
|
426
|
-
* Export all memories for backup/portability
|
|
427
|
-
*/
|
|
428
|
-
export() {
|
|
429
|
-
return this.memories.map((m) => ({ ...m }));
|
|
430
473
|
}
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
}
|
|
440
|
-
/**
|
|
441
|
-
* Load memories from localStorage (browser) or skip (Node)
|
|
442
|
-
*/
|
|
443
|
-
loadFromStorage() {
|
|
444
|
-
if (typeof window === "undefined" || !window.localStorage) return;
|
|
445
|
-
try {
|
|
446
|
-
const stored = localStorage.getItem(this.config.storageKey);
|
|
447
|
-
if (stored) {
|
|
448
|
-
this.memories = JSON.parse(stored);
|
|
449
|
-
}
|
|
450
|
-
} catch (e) {
|
|
451
|
-
console.warn("Failed to load memories from storage:", e);
|
|
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}`);
|
|
452
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}`);
|
|
453
494
|
}
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
}
|
|
462
|
-
|
|
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}`);
|
|
463
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 [];
|
|
464
516
|
}
|
|
465
517
|
};
|
|
466
|
-
var
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
async recall(query, limit) {
|
|
476
|
-
return rankMemoriesByRelevance(this.memories, query, limit);
|
|
477
|
-
}
|
|
478
|
-
async list(limit, offset) {
|
|
479
|
-
return this.memories.slice(offset ?? 0, (offset ?? 0) + (limit ?? 50));
|
|
480
|
-
}
|
|
481
|
-
async clear() {
|
|
482
|
-
this.memories = [];
|
|
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}`);
|
|
483
527
|
}
|
|
484
|
-
|
|
485
|
-
|
|
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);
|
|
486
571
|
}
|
|
487
|
-
|
|
488
|
-
|
|
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;
|
|
489
587
|
}
|
|
490
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");
|
|
655
|
+
try {
|
|
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;
|
|
668
|
+
}
|
|
669
|
+
};
|
|
670
|
+
return { store, recall, list, clear };
|
|
671
|
+
};
|
|
672
|
+
var createMemory = (config = {}) => createLanceDBMemory(config);
|
|
491
673
|
|
|
492
674
|
// src/bridge.ts
|
|
493
675
|
var movementRule = {
|
|
@@ -658,30 +840,40 @@ var DEFAULT_RULES = [
|
|
|
658
840
|
resourceRule
|
|
659
841
|
];
|
|
660
842
|
var createBridge = (config = {}) => {
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
this.rules = /* @__PURE__ */ new Map();
|
|
670
|
-
DEFAULT_RULES.forEach((rule) => this.rules.set(rule.id, rule));
|
|
671
|
-
if (config.customRules) {
|
|
672
|
-
config.customRules.forEach((rule) => this.rules.set(rule.id, rule));
|
|
673
|
-
}
|
|
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));
|
|
674
851
|
}
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
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(
|
|
680
872
|
(rule) => rule.actionTypes.some(
|
|
681
873
|
(t) => t.toLowerCase() === action.type.toLowerCase()
|
|
682
874
|
)
|
|
683
875
|
);
|
|
684
|
-
if (
|
|
876
|
+
if (effectiveConfig.strictMode && applicableRules.length === 0) {
|
|
685
877
|
const safeTypes = ["IDLE", "idle", "WAIT", "wait"];
|
|
686
878
|
if (!safeTypes.includes(action.type)) {
|
|
687
879
|
return {
|
|
@@ -695,9 +887,9 @@ var BridgeImpl = class {
|
|
|
695
887
|
for (const rule of applicableRules) {
|
|
696
888
|
const result = rule.validate(currentAction, context);
|
|
697
889
|
if (!result.valid) {
|
|
698
|
-
if (
|
|
890
|
+
if (effectiveConfig.apiUrl) {
|
|
699
891
|
try {
|
|
700
|
-
const apiResult = await
|
|
892
|
+
const apiResult = await validateRemote(action);
|
|
701
893
|
console.debug("API validation result:", apiResult);
|
|
702
894
|
} catch (e) {
|
|
703
895
|
console.warn("Failed to validate via API:", e);
|
|
@@ -713,59 +905,22 @@ var BridgeImpl = class {
|
|
|
713
905
|
valid: true,
|
|
714
906
|
correctedAction: currentAction !== action ? currentAction : void 0
|
|
715
907
|
};
|
|
716
|
-
}
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
const data = await response.json();
|
|
733
|
-
return {
|
|
734
|
-
valid: data.valid,
|
|
735
|
-
reason: data.reason
|
|
736
|
-
};
|
|
737
|
-
}
|
|
738
|
-
/**
|
|
739
|
-
* Register a custom validation rule
|
|
740
|
-
*/
|
|
741
|
-
registerRule(rule) {
|
|
742
|
-
this.rules.set(rule.id, rule);
|
|
743
|
-
}
|
|
744
|
-
/**
|
|
745
|
-
* List all registered rules
|
|
746
|
-
*/
|
|
747
|
-
listRules() {
|
|
748
|
-
return Array.from(this.rules.values());
|
|
749
|
-
}
|
|
750
|
-
/**
|
|
751
|
-
* Remove a rule by ID
|
|
752
|
-
*/
|
|
753
|
-
removeRule(ruleId) {
|
|
754
|
-
return this.rules.delete(ruleId);
|
|
755
|
-
}
|
|
756
|
-
};
|
|
757
|
-
var MockBridge = class {
|
|
758
|
-
async validate(action) {
|
|
759
|
-
return { valid: true };
|
|
760
|
-
}
|
|
761
|
-
registerRule(_rule) {
|
|
762
|
-
}
|
|
763
|
-
listRules() {
|
|
764
|
-
return [];
|
|
765
|
-
}
|
|
766
|
-
removeRule(_ruleId) {
|
|
767
|
-
return false;
|
|
768
|
-
}
|
|
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
|
+
};
|
|
769
924
|
};
|
|
770
925
|
var validateAction = (action, rules, context = {}) => {
|
|
771
926
|
for (const rule of rules) {
|
|
@@ -779,132 +934,6 @@ var validateAction = (action, rules, context = {}) => {
|
|
|
779
934
|
return { valid: true };
|
|
780
935
|
};
|
|
781
936
|
|
|
782
|
-
// src/soul.ts
|
|
783
|
-
var createSoul = (id, name, persona, state, memories = []) => {
|
|
784
|
-
return {
|
|
785
|
-
id,
|
|
786
|
-
version: "1.0.0",
|
|
787
|
-
name,
|
|
788
|
-
persona,
|
|
789
|
-
state: { ...state },
|
|
790
|
-
memories: [...memories]
|
|
791
|
-
};
|
|
792
|
-
};
|
|
793
|
-
var serializeSoul = (soul) => {
|
|
794
|
-
return JSON.stringify(soul, null, 2);
|
|
795
|
-
};
|
|
796
|
-
var deserializeSoul = (json) => {
|
|
797
|
-
const parsed = JSON.parse(json);
|
|
798
|
-
if (!parsed.id || !parsed.persona || !parsed.state) {
|
|
799
|
-
throw new Error("Invalid Soul format: missing required fields");
|
|
800
|
-
}
|
|
801
|
-
return {
|
|
802
|
-
id: parsed.id,
|
|
803
|
-
version: parsed.version || "1.0.0",
|
|
804
|
-
name: parsed.name || "Unknown",
|
|
805
|
-
persona: parsed.persona,
|
|
806
|
-
state: parsed.state,
|
|
807
|
-
memories: parsed.memories || [],
|
|
808
|
-
signature: parsed.signature
|
|
809
|
-
};
|
|
810
|
-
};
|
|
811
|
-
var validateSoul = (soul) => {
|
|
812
|
-
const errors = [];
|
|
813
|
-
if (!soul.id) errors.push("Missing id");
|
|
814
|
-
if (!soul.persona) errors.push("Missing persona");
|
|
815
|
-
if (!soul.state) errors.push("Missing state");
|
|
816
|
-
if (!soul.state?.mood) errors.push("Missing state.mood");
|
|
817
|
-
return {
|
|
818
|
-
valid: errors.length === 0,
|
|
819
|
-
errors
|
|
820
|
-
};
|
|
821
|
-
};
|
|
822
|
-
var exportSoulToIPFS = async (agentId, soul, config = {}) => {
|
|
823
|
-
const apiUrl = config.apiUrl || "https://forbocai-api.onrender.com";
|
|
824
|
-
try {
|
|
825
|
-
const response = await fetch(`${apiUrl}/agents/${agentId}/soul/export`, {
|
|
826
|
-
method: "POST",
|
|
827
|
-
headers: { "Content-Type": "application/json" },
|
|
828
|
-
body: JSON.stringify({ agentIdRef: agentId })
|
|
829
|
-
});
|
|
830
|
-
if (!response.ok) {
|
|
831
|
-
throw new Error(`Export failed: ${response.statusText}`);
|
|
832
|
-
}
|
|
833
|
-
const data = await response.json();
|
|
834
|
-
return {
|
|
835
|
-
cid: data.cid,
|
|
836
|
-
ipfsUrl: data.ipfsUrl,
|
|
837
|
-
signature: data.signature,
|
|
838
|
-
soul
|
|
839
|
-
};
|
|
840
|
-
} catch (e) {
|
|
841
|
-
const mockCid = `Qm${Buffer.from(soul.id).toString("base64").substring(0, 44)}`;
|
|
842
|
-
return {
|
|
843
|
-
cid: mockCid,
|
|
844
|
-
ipfsUrl: `ipfs://${mockCid}`,
|
|
845
|
-
soul
|
|
846
|
-
};
|
|
847
|
-
}
|
|
848
|
-
};
|
|
849
|
-
var importSoulFromIPFS = async (cid, config = {}) => {
|
|
850
|
-
const apiUrl = config.apiUrl || "https://forbocai-api.onrender.com";
|
|
851
|
-
try {
|
|
852
|
-
const response = await fetch(`${apiUrl}/souls/${cid}`, {
|
|
853
|
-
method: "GET",
|
|
854
|
-
headers: { "Content-Type": "application/json" }
|
|
855
|
-
});
|
|
856
|
-
if (!response.ok) {
|
|
857
|
-
throw new Error(`Import failed: ${response.statusText}`);
|
|
858
|
-
}
|
|
859
|
-
const data = await response.json();
|
|
860
|
-
return {
|
|
861
|
-
id: data.soulId,
|
|
862
|
-
version: "1.0.0",
|
|
863
|
-
name: data.soulName,
|
|
864
|
-
persona: data.dna,
|
|
865
|
-
// DNA contains the persona
|
|
866
|
-
state: { mood: "neutral", inventory: [], skills: {}, relationships: {} },
|
|
867
|
-
memories: []
|
|
868
|
-
};
|
|
869
|
-
} catch (e) {
|
|
870
|
-
throw new Error(`Failed to import Soul from CID ${cid}: ${e}`);
|
|
871
|
-
}
|
|
872
|
-
};
|
|
873
|
-
var createAgentFromSoul = async (cid, cortexId, config = {}) => {
|
|
874
|
-
const apiUrl = config.apiUrl || "https://forbocai-api.onrender.com";
|
|
875
|
-
const response = await fetch(`${apiUrl}/agents/import`, {
|
|
876
|
-
method: "POST",
|
|
877
|
-
headers: { "Content-Type": "application/json" },
|
|
878
|
-
body: JSON.stringify({ cidRef: cid })
|
|
879
|
-
});
|
|
880
|
-
if (!response.ok) {
|
|
881
|
-
throw new Error(`Failed to create agent from Soul: ${response.statusText}`);
|
|
882
|
-
}
|
|
883
|
-
const data = await response.json();
|
|
884
|
-
return {
|
|
885
|
-
agentId: data.agentId,
|
|
886
|
-
persona: data.persona
|
|
887
|
-
};
|
|
888
|
-
};
|
|
889
|
-
var SoulImpl = class {
|
|
890
|
-
constructor(id, name, persona, state, memories = [], apiUrl) {
|
|
891
|
-
this.soul = createSoul(id, name, persona, state, memories);
|
|
892
|
-
this.apiUrl = apiUrl || "https://forbocai-api.onrender.com";
|
|
893
|
-
}
|
|
894
|
-
async export(config) {
|
|
895
|
-
return exportSoulToIPFS(this.soul.id, this.soul, {
|
|
896
|
-
...config,
|
|
897
|
-
apiUrl: config?.apiUrl || this.apiUrl
|
|
898
|
-
});
|
|
899
|
-
}
|
|
900
|
-
toJSON() {
|
|
901
|
-
return { ...this.soul };
|
|
902
|
-
}
|
|
903
|
-
};
|
|
904
|
-
var createSoulInstance = (id, name, persona, state, memories, apiUrl) => {
|
|
905
|
-
return new SoulImpl(id, name, persona, state, memories, apiUrl);
|
|
906
|
-
};
|
|
907
|
-
|
|
908
937
|
// src/ghost.ts
|
|
909
938
|
var startGhostSession = async (config) => {
|
|
910
939
|
const apiUrl = config.apiUrl || "https://forbocai-api.onrender.com";
|
|
@@ -1030,80 +1059,119 @@ var waitForGhostCompletion = async (sessionId, pollIntervalMs = 5e3, timeoutMs =
|
|
|
1030
1059
|
}
|
|
1031
1060
|
throw new Error(`Ghost session timed out after ${timeoutMs}ms`);
|
|
1032
1061
|
};
|
|
1033
|
-
var
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
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
|
+
};
|
|
1038
1082
|
}
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
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 [];
|
|
1043
1105
|
}
|
|
1044
|
-
|
|
1045
|
-
|
|
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) {
|
|
1046
1117
|
throw new Error("Ghost session not started");
|
|
1047
1118
|
}
|
|
1048
|
-
return getGhostStatus(
|
|
1049
|
-
}
|
|
1050
|
-
async
|
|
1051
|
-
if (!
|
|
1119
|
+
return getGhostStatus(sessionId, apiUrl);
|
|
1120
|
+
};
|
|
1121
|
+
const results = async () => {
|
|
1122
|
+
if (!sessionId) {
|
|
1052
1123
|
throw new Error("Ghost session not started");
|
|
1053
1124
|
}
|
|
1054
|
-
return getGhostResults(
|
|
1055
|
-
}
|
|
1056
|
-
async
|
|
1057
|
-
|
|
1058
|
-
}
|
|
1059
|
-
async
|
|
1060
|
-
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) {
|
|
1061
1132
|
throw new Error("Ghost session not started");
|
|
1062
1133
|
}
|
|
1063
1134
|
return waitForGhostCompletion(
|
|
1064
|
-
|
|
1135
|
+
sessionId,
|
|
1065
1136
|
pollIntervalMs,
|
|
1066
1137
|
timeoutMs,
|
|
1067
|
-
|
|
1138
|
+
apiUrl,
|
|
1068
1139
|
onProgress
|
|
1069
1140
|
);
|
|
1070
|
-
}
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
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
|
+
};
|
|
1074
1154
|
};
|
|
1075
1155
|
|
|
1076
1156
|
// src/index.ts
|
|
1077
1157
|
var init = () => {
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
${dim}> Connecting into the Neuro-Symbolic Grid...${reset}
|
|
1092
|
-
${dim}> Status: ${cyan}ONLINE${reset}
|
|
1093
|
-
|
|
1094
|
-
${cyan}Welcome to the Future of NPC Intelligence.${reset}
|
|
1095
|
-
`);
|
|
1158
|
+
console.log(`
|
|
1159
|
+
\x1B[36m _ _ _ ___ _ _
|
|
1160
|
+
| \\| |___ _ _ _ __ ___ / \\ |_ _|_ _ | |_ (_|
|
|
1161
|
+
| . / -_) || | '_ / _ \\/ _ \\ | || ' \\| _| | |
|
|
1162
|
+
|_|\\_\\___|\\_,_| ._\\___/_/ \\_\\ |___|_||_|\\__|_|_|
|
|
1163
|
+
|_| \x1B[0m
|
|
1164
|
+
Neuro-Symbolic Grid SDK v0.3.2
|
|
1165
|
+
---------------------------------
|
|
1166
|
+
Vessel: ACTIVE
|
|
1167
|
+
Memory: LOCAL (LanceDB)
|
|
1168
|
+
Smart: LOCAL (GGUF)
|
|
1169
|
+
Grid: CONNECTED
|
|
1170
|
+
`);
|
|
1096
1171
|
};
|
|
1097
1172
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1098
1173
|
0 && (module.exports = {
|
|
1099
|
-
GhostSession,
|
|
1100
|
-
MockBridge,
|
|
1101
|
-
MockCortex,
|
|
1102
|
-
MockMemory,
|
|
1103
|
-
SoulImpl,
|
|
1104
|
-
applyTemporalDecay,
|
|
1105
1174
|
attackRule,
|
|
1106
|
-
computeSimilarity,
|
|
1107
1175
|
createAgent,
|
|
1108
1176
|
createAgentFromSoul,
|
|
1109
1177
|
createBridge,
|
|
@@ -1114,25 +1182,28 @@ ${cyan}Welcome to the Future of NPC Intelligence.${reset}
|
|
|
1114
1182
|
createMemoryItem,
|
|
1115
1183
|
createSoul,
|
|
1116
1184
|
createSoulInstance,
|
|
1185
|
+
createTable,
|
|
1117
1186
|
deserializeSoul,
|
|
1118
1187
|
exportSoulToIPFS,
|
|
1119
1188
|
exportToSoul,
|
|
1120
1189
|
fromSoul,
|
|
1121
|
-
|
|
1122
|
-
|
|
1190
|
+
generateEmbedding,
|
|
1191
|
+
getGhostHistory,
|
|
1123
1192
|
getGhostResults,
|
|
1124
1193
|
getGhostStatus,
|
|
1194
|
+
getSoulList,
|
|
1195
|
+
getVectorTable,
|
|
1125
1196
|
importSoulFromIPFS,
|
|
1126
1197
|
init,
|
|
1198
|
+
initVectorEngine,
|
|
1127
1199
|
interactRule,
|
|
1128
1200
|
movementRule,
|
|
1129
1201
|
processAgentInput,
|
|
1130
|
-
processObservationToDirective,
|
|
1131
|
-
rankMemoriesByRelevance,
|
|
1132
1202
|
resourceRule,
|
|
1133
1203
|
serializeSoul,
|
|
1134
1204
|
speakRule,
|
|
1135
1205
|
startGhostSession,
|
|
1206
|
+
stopGhostSession,
|
|
1136
1207
|
updateAgentState,
|
|
1137
1208
|
validateAction,
|
|
1138
1209
|
validateSoul,
|