@forbocai/core 0.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +597 -0
- package/dist/index.d.ts +597 -0
- package/dist/index.js +960 -0
- package/dist/index.mjs +903 -0
- package/package.json +28 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,960 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
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
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
RemoteCortex: () => RemoteCortex,
|
|
34
|
+
SDK_VERSION: () => SDK_VERSION,
|
|
35
|
+
createAgent: () => createAgent,
|
|
36
|
+
createBridge: () => createBridge,
|
|
37
|
+
createGhost: () => createGhost,
|
|
38
|
+
createInitialState: () => createInitialState,
|
|
39
|
+
createSoul: () => createSoul,
|
|
40
|
+
createSoulInstance: () => createSoulInstance,
|
|
41
|
+
deserializeSoul: () => deserializeSoul,
|
|
42
|
+
exportSoul: () => exportSoul,
|
|
43
|
+
exportToSoul: () => exportToSoul,
|
|
44
|
+
fromSoul: () => fromSoul,
|
|
45
|
+
getGhostHistory: () => getGhostHistory,
|
|
46
|
+
getGhostResults: () => getGhostResults,
|
|
47
|
+
getGhostStatus: () => getGhostStatus,
|
|
48
|
+
getSoulList: () => getSoulList,
|
|
49
|
+
importSoulFromArweave: () => importSoulFromArweave,
|
|
50
|
+
presets: () => presets_exports,
|
|
51
|
+
processAgentInput: () => processAgentInput,
|
|
52
|
+
serializeSoul: () => serializeSoul,
|
|
53
|
+
startGhostSession: () => startGhostSession,
|
|
54
|
+
stopGhostSession: () => stopGhostSession,
|
|
55
|
+
updateAgentState: () => updateAgentState,
|
|
56
|
+
uploadToArweave: () => uploadToArweave,
|
|
57
|
+
validateAction: () => validateAction,
|
|
58
|
+
validateSoul: () => validateSoul,
|
|
59
|
+
waitForGhostCompletion: () => waitForGhostCompletion
|
|
60
|
+
});
|
|
61
|
+
module.exports = __toCommonJS(index_exports);
|
|
62
|
+
|
|
63
|
+
// src/agent.ts
|
|
64
|
+
var createInitialState = (partial) => {
|
|
65
|
+
return { ...partial };
|
|
66
|
+
};
|
|
67
|
+
var updateAgentState = (currentState, updates) => {
|
|
68
|
+
return {
|
|
69
|
+
...currentState,
|
|
70
|
+
...updates
|
|
71
|
+
};
|
|
72
|
+
};
|
|
73
|
+
var processAgentInput = (currentState, input, context = {}) => {
|
|
74
|
+
const stateKeys = Object.keys(currentState);
|
|
75
|
+
const stateSummary = stateKeys.length > 0 ? stateKeys.map((k) => `${k}=${JSON.stringify(currentState[k])}`).join(", ") : "empty";
|
|
76
|
+
return {
|
|
77
|
+
dialogue: `Processing: "${input}" (State: ${stateSummary})`,
|
|
78
|
+
action: {
|
|
79
|
+
type: "respond",
|
|
80
|
+
reason: "Default processing"
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
};
|
|
84
|
+
var exportToSoul = (agentId, name, persona, state, memories) => {
|
|
85
|
+
return {
|
|
86
|
+
id: agentId,
|
|
87
|
+
version: "1.0.0",
|
|
88
|
+
name,
|
|
89
|
+
persona,
|
|
90
|
+
memories: [...memories],
|
|
91
|
+
// Copy array for immutability
|
|
92
|
+
state: { ...state }
|
|
93
|
+
// Copy state for immutability
|
|
94
|
+
};
|
|
95
|
+
};
|
|
96
|
+
var createAgent = (config) => {
|
|
97
|
+
let state = createInitialState(config.initialState);
|
|
98
|
+
let memories = [];
|
|
99
|
+
const cortex = config.cortex;
|
|
100
|
+
const apiUrl = config.apiUrl || "http://localhost:8080";
|
|
101
|
+
const agentId = config.id || "agent-" + Math.random().toString(36).substring(7);
|
|
102
|
+
const getAgentState = () => {
|
|
103
|
+
return { ...state };
|
|
104
|
+
};
|
|
105
|
+
const setAgentState = (newState) => {
|
|
106
|
+
state = newState;
|
|
107
|
+
};
|
|
108
|
+
const process = async (input, context = {}) => {
|
|
109
|
+
const currentState = getAgentState();
|
|
110
|
+
const apiContext = Object.entries(context).map(([k, v]) => [k, String(v)]);
|
|
111
|
+
const timestamp = Date.now();
|
|
112
|
+
apiContext.push(["timestamp", String(timestamp)]);
|
|
113
|
+
let relevantMemories = [];
|
|
114
|
+
if (config.memory && typeof config.memory.recall === "function") {
|
|
115
|
+
try {
|
|
116
|
+
relevantMemories = await config.memory.recall(input, 5);
|
|
117
|
+
} catch {
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
let directive = "Respond normally.";
|
|
121
|
+
let instruction = "IDLE";
|
|
122
|
+
let target;
|
|
123
|
+
try {
|
|
124
|
+
const dirRes = await fetch(`${apiUrl}/agents/${agentId}/directive`, {
|
|
125
|
+
method: "POST",
|
|
126
|
+
headers: {
|
|
127
|
+
"Content-Type": "application/json",
|
|
128
|
+
"X-Request-Timestamp": String(timestamp)
|
|
129
|
+
},
|
|
130
|
+
body: JSON.stringify({
|
|
131
|
+
dirContext: apiContext,
|
|
132
|
+
dirState: currentState,
|
|
133
|
+
dirMemories: relevantMemories.map((m) => ({ text: m.text, type: m.type, importance: m.importance }))
|
|
134
|
+
})
|
|
135
|
+
});
|
|
136
|
+
if (dirRes.ok) {
|
|
137
|
+
const data = await dirRes.json();
|
|
138
|
+
directive = data.dirReason;
|
|
139
|
+
instruction = data.dirInstruction;
|
|
140
|
+
target = data.dirTarget;
|
|
141
|
+
} else {
|
|
142
|
+
throw new Error(`API Error: ${dirRes.status}`);
|
|
143
|
+
}
|
|
144
|
+
} catch (e) {
|
|
145
|
+
throw new Error(`API Directive Failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
146
|
+
}
|
|
147
|
+
const memoryContext = relevantMemories.length > 0 ? `
|
|
148
|
+
Memories:
|
|
149
|
+
${relevantMemories.map((m) => `- [${m.type}] ${m.text}`).join("\n")}
|
|
150
|
+
` : "";
|
|
151
|
+
const prompt = `System: You are ${config.persona}.
|
|
152
|
+
Directive: ${instruction} (${directive}).
|
|
153
|
+
Target: ${target || "None"}${memoryContext}
|
|
154
|
+
User: ${input}
|
|
155
|
+
Agent:`;
|
|
156
|
+
const generatedText = await cortex.complete(prompt);
|
|
157
|
+
const verRes = await fetch(`${apiUrl}/agents/${agentId}/verdict`, {
|
|
158
|
+
method: "POST",
|
|
159
|
+
headers: { "Content-Type": "application/json" },
|
|
160
|
+
body: JSON.stringify({
|
|
161
|
+
verDirective: {
|
|
162
|
+
dirInstruction: instruction,
|
|
163
|
+
dirTarget: target || "",
|
|
164
|
+
dirReason: directive
|
|
165
|
+
},
|
|
166
|
+
verAction: {
|
|
167
|
+
gaType: instruction,
|
|
168
|
+
// Map to generic action type
|
|
169
|
+
actionReason: directive,
|
|
170
|
+
actionTarget: target || ""
|
|
171
|
+
},
|
|
172
|
+
verThought: generatedText
|
|
173
|
+
})
|
|
174
|
+
});
|
|
175
|
+
if (!verRes.ok) {
|
|
176
|
+
throw new Error(`API Verdict Error: ${verRes.status}`);
|
|
177
|
+
}
|
|
178
|
+
const vData = await verRes.json();
|
|
179
|
+
const isValid = vData.verValid;
|
|
180
|
+
const signature = vData.verSignature;
|
|
181
|
+
if (!isValid) {
|
|
182
|
+
return {
|
|
183
|
+
dialogue: "... [Action Blocked by Protocol]",
|
|
184
|
+
action: { type: "BLOCKED", reason: "API Validation Failed" }
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
if (config.memory && typeof config.memory.store === "function") {
|
|
188
|
+
config.memory.store(`Input: "${input}" \u2192 Action: ${instruction}`, "experience", 0.5).catch(() => {
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
return {
|
|
192
|
+
dialogue: generatedText,
|
|
193
|
+
action: {
|
|
194
|
+
type: instruction,
|
|
195
|
+
reason: directive,
|
|
196
|
+
target,
|
|
197
|
+
signature
|
|
198
|
+
},
|
|
199
|
+
thought: `Directive: ${directive}`
|
|
200
|
+
};
|
|
201
|
+
};
|
|
202
|
+
const exportSoul2 = async () => {
|
|
203
|
+
let exportedMemories = [...memories];
|
|
204
|
+
if (config.memory && typeof config.memory.export === "function") {
|
|
205
|
+
try {
|
|
206
|
+
exportedMemories = await config.memory.export();
|
|
207
|
+
} catch {
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return exportToSoul(agentId, "Agent", config.persona, state, exportedMemories);
|
|
211
|
+
};
|
|
212
|
+
return {
|
|
213
|
+
process,
|
|
214
|
+
getState: getAgentState,
|
|
215
|
+
setState: setAgentState,
|
|
216
|
+
export: exportSoul2
|
|
217
|
+
};
|
|
218
|
+
};
|
|
219
|
+
var fromSoul = async (soul, cortex, memory) => {
|
|
220
|
+
const agent = createAgent({
|
|
221
|
+
id: soul.id,
|
|
222
|
+
cortex,
|
|
223
|
+
memory: memory || null,
|
|
224
|
+
persona: soul.persona,
|
|
225
|
+
initialState: soul.state
|
|
226
|
+
});
|
|
227
|
+
if (memory && soul.memories && soul.memories.length > 0 && typeof memory.import === "function") {
|
|
228
|
+
try {
|
|
229
|
+
await memory.import(soul.memories);
|
|
230
|
+
} catch (e) {
|
|
231
|
+
console.warn("Failed to import memories into module:", e);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
return agent;
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
// src/bridge.ts
|
|
238
|
+
var createBridge = (config = {}) => {
|
|
239
|
+
const effectiveConfig = {
|
|
240
|
+
strictMode: config.strictMode ?? false,
|
|
241
|
+
...config
|
|
242
|
+
};
|
|
243
|
+
const rules = /* @__PURE__ */ new Map();
|
|
244
|
+
if (config.customRules) {
|
|
245
|
+
config.customRules.forEach((rule) => rules.set(rule.id, rule));
|
|
246
|
+
}
|
|
247
|
+
const validateRemote = async (action) => {
|
|
248
|
+
const response = await fetch(`${effectiveConfig.apiUrl}/bridge/validate`, {
|
|
249
|
+
method: "POST",
|
|
250
|
+
headers: { "Content-Type": "application/json" },
|
|
251
|
+
body: JSON.stringify({
|
|
252
|
+
actionType: action.type,
|
|
253
|
+
payload: JSON.stringify(action.payload || {})
|
|
254
|
+
})
|
|
255
|
+
});
|
|
256
|
+
if (!response.ok) {
|
|
257
|
+
throw new Error(`API validation failed: ${response.statusText}`);
|
|
258
|
+
}
|
|
259
|
+
const data = await response.json();
|
|
260
|
+
return {
|
|
261
|
+
valid: data.valid,
|
|
262
|
+
reason: data.reason
|
|
263
|
+
};
|
|
264
|
+
};
|
|
265
|
+
const validate = async (action, context = {}) => {
|
|
266
|
+
const applicableRules = Array.from(rules.values()).filter(
|
|
267
|
+
(rule) => rule.actionTypes.some(
|
|
268
|
+
(t) => t.toLowerCase() === action.type.toLowerCase()
|
|
269
|
+
)
|
|
270
|
+
);
|
|
271
|
+
if (effectiveConfig.strictMode && applicableRules.length === 0) {
|
|
272
|
+
const safeTypes = ["IDLE", "idle", "WAIT", "wait"];
|
|
273
|
+
if (!safeTypes.includes(action.type)) {
|
|
274
|
+
return {
|
|
275
|
+
valid: false,
|
|
276
|
+
reason: `Unknown action type '${action.type}' in strict mode`,
|
|
277
|
+
correctedAction: { ...action, type: "IDLE", reason: "Unknown action type" }
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
let currentAction = action;
|
|
282
|
+
for (const rule of applicableRules) {
|
|
283
|
+
const result = rule.validate(currentAction, context);
|
|
284
|
+
if (!result.valid) {
|
|
285
|
+
if (effectiveConfig.apiUrl) {
|
|
286
|
+
const apiResult = await validateRemote(action);
|
|
287
|
+
console.debug("API validation result:", apiResult);
|
|
288
|
+
}
|
|
289
|
+
return result;
|
|
290
|
+
}
|
|
291
|
+
if (result.correctedAction) {
|
|
292
|
+
currentAction = result.correctedAction;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
return {
|
|
296
|
+
valid: true,
|
|
297
|
+
correctedAction: currentAction !== action ? currentAction : void 0
|
|
298
|
+
};
|
|
299
|
+
};
|
|
300
|
+
const registerRule = (rule) => {
|
|
301
|
+
rules.set(rule.id, rule);
|
|
302
|
+
if (effectiveConfig.apiUrl) {
|
|
303
|
+
}
|
|
304
|
+
};
|
|
305
|
+
const listRules = () => {
|
|
306
|
+
return Array.from(rules.values());
|
|
307
|
+
};
|
|
308
|
+
const removeRule = (ruleId) => {
|
|
309
|
+
return rules.delete(ruleId);
|
|
310
|
+
};
|
|
311
|
+
return {
|
|
312
|
+
validate,
|
|
313
|
+
registerRule,
|
|
314
|
+
listRules,
|
|
315
|
+
removeRule
|
|
316
|
+
};
|
|
317
|
+
};
|
|
318
|
+
var validateAction = (action, rules, context = {}) => {
|
|
319
|
+
for (const rule of rules) {
|
|
320
|
+
if (rule.actionTypes.some((t) => t.toLowerCase() === action.type.toLowerCase())) {
|
|
321
|
+
const result = rule.validate(action, context);
|
|
322
|
+
if (!result.valid) {
|
|
323
|
+
return result;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
return { valid: true };
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
// src/soul.ts
|
|
331
|
+
var import_sdk = __toESM(require("@irys/sdk"));
|
|
332
|
+
var createSoul = (id, name, persona, state, memories = []) => {
|
|
333
|
+
return {
|
|
334
|
+
id,
|
|
335
|
+
version: "1.0.0",
|
|
336
|
+
name,
|
|
337
|
+
persona,
|
|
338
|
+
state: { ...state },
|
|
339
|
+
memories: [...memories]
|
|
340
|
+
};
|
|
341
|
+
};
|
|
342
|
+
var serializeSoul = (soul) => {
|
|
343
|
+
return JSON.stringify(soul, null, 2);
|
|
344
|
+
};
|
|
345
|
+
var deserializeSoul = (json) => {
|
|
346
|
+
const parsed = JSON.parse(json);
|
|
347
|
+
if (!parsed.id || !parsed.persona || !parsed.state) {
|
|
348
|
+
throw new Error("Invalid Soul format: missing required fields");
|
|
349
|
+
}
|
|
350
|
+
return {
|
|
351
|
+
id: parsed.id,
|
|
352
|
+
version: parsed.version || "1.0.0",
|
|
353
|
+
name: parsed.name || "Unknown",
|
|
354
|
+
persona: parsed.persona,
|
|
355
|
+
state: parsed.state,
|
|
356
|
+
memories: parsed.memories || [],
|
|
357
|
+
signature: parsed.signature
|
|
358
|
+
};
|
|
359
|
+
};
|
|
360
|
+
var uploadToArweave = async (soul, config) => {
|
|
361
|
+
if (!config.privateKey) {
|
|
362
|
+
throw new Error("Private key required for direct Arweave upload");
|
|
363
|
+
}
|
|
364
|
+
const irys = new import_sdk.default({
|
|
365
|
+
url: config.irysUrl || "https://node1.irys.xyz",
|
|
366
|
+
token: config.token || "solana",
|
|
367
|
+
key: config.privateKey
|
|
368
|
+
});
|
|
369
|
+
const dataToUpload = serializeSoul(soul);
|
|
370
|
+
try {
|
|
371
|
+
const receipt = await irys.upload(dataToUpload);
|
|
372
|
+
return {
|
|
373
|
+
txId: receipt.id,
|
|
374
|
+
url: `https://gateway.irys.xyz/${receipt.id}`,
|
|
375
|
+
soul
|
|
376
|
+
};
|
|
377
|
+
} catch (e) {
|
|
378
|
+
throw new Error(`Failed to upload to Arweave: ${e}`);
|
|
379
|
+
}
|
|
380
|
+
};
|
|
381
|
+
var exportSoul = async (agentId, soul, config = {}) => {
|
|
382
|
+
if (config.privateKey) {
|
|
383
|
+
try {
|
|
384
|
+
return await uploadToArweave(soul, config);
|
|
385
|
+
} catch (e) {
|
|
386
|
+
console.warn("Direct Arweave upload failed, falling back to API", e);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
const apiUrl = config.apiUrl || "https://api.forboc.ai";
|
|
390
|
+
try {
|
|
391
|
+
const response = await fetch(`${apiUrl}/agents/${agentId}/soul/export`, {
|
|
392
|
+
method: "POST",
|
|
393
|
+
headers: { "Content-Type": "application/json" },
|
|
394
|
+
body: JSON.stringify({ soul })
|
|
395
|
+
// Sending full soul for server to sign/upload
|
|
396
|
+
});
|
|
397
|
+
if (!response.ok) {
|
|
398
|
+
throw new Error(`Export failed: ${response.statusText}`);
|
|
399
|
+
}
|
|
400
|
+
const data = await response.json();
|
|
401
|
+
return {
|
|
402
|
+
txId: data.txId,
|
|
403
|
+
url: data.url,
|
|
404
|
+
soul
|
|
405
|
+
};
|
|
406
|
+
} catch (e) {
|
|
407
|
+
console.warn("API export failed, returning mock Arweave TXID used for dev");
|
|
408
|
+
const mockTxId = `mock_ar_${Date.now()}`;
|
|
409
|
+
return {
|
|
410
|
+
txId: mockTxId,
|
|
411
|
+
url: `https://arweave.net/${mockTxId}`,
|
|
412
|
+
soul
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
};
|
|
416
|
+
var importSoulFromArweave = async (txId, config = {}) => {
|
|
417
|
+
const gateway = config.gatewayUrl || "https://gateway.irys.xyz";
|
|
418
|
+
try {
|
|
419
|
+
const response = await fetch(`${gateway}/${txId}`, {
|
|
420
|
+
method: "GET",
|
|
421
|
+
headers: { "Content-Type": "application/json" }
|
|
422
|
+
});
|
|
423
|
+
if (!response.ok) {
|
|
424
|
+
throw new Error(`Import failed: ${response.statusText}`);
|
|
425
|
+
}
|
|
426
|
+
const data = await response.json();
|
|
427
|
+
if (!data.id || !data.persona) {
|
|
428
|
+
throw new Error("Invalid soul data from Arweave");
|
|
429
|
+
}
|
|
430
|
+
return data;
|
|
431
|
+
} catch (e) {
|
|
432
|
+
throw new Error(`Failed to import Soul from TXID ${txId}: ${e}`);
|
|
433
|
+
}
|
|
434
|
+
};
|
|
435
|
+
var getSoulList = async (limit = 50, apiUrl) => {
|
|
436
|
+
const url = apiUrl || "https://api.forboc.ai";
|
|
437
|
+
try {
|
|
438
|
+
const response = await fetch(`${url}/souls?limit=${limit}`);
|
|
439
|
+
if (!response.ok) throw new Error(response.statusText);
|
|
440
|
+
const data = await response.json();
|
|
441
|
+
return data.souls.map((s) => ({
|
|
442
|
+
txId: s.txId,
|
|
443
|
+
name: s.name,
|
|
444
|
+
agentId: s.agentId,
|
|
445
|
+
exportedAt: s.exportedAt,
|
|
446
|
+
url: s.url || `https://gateway.irys.xyz/${s.txId}`
|
|
447
|
+
}));
|
|
448
|
+
} catch (e) {
|
|
449
|
+
return [];
|
|
450
|
+
}
|
|
451
|
+
};
|
|
452
|
+
var createSoulInstance = (id, name, persona, state, memories = [], initialApiUrl) => {
|
|
453
|
+
const soulData = createSoul(id, name, persona, state, memories);
|
|
454
|
+
const defaultApiUrl = initialApiUrl || "https://api.forboc.ai";
|
|
455
|
+
const performExport = async (config) => {
|
|
456
|
+
return exportSoul(soulData.id, soulData, {
|
|
457
|
+
...config,
|
|
458
|
+
apiUrl: config?.apiUrl || defaultApiUrl
|
|
459
|
+
});
|
|
460
|
+
};
|
|
461
|
+
return {
|
|
462
|
+
export: performExport,
|
|
463
|
+
toJSON: () => ({ ...soulData })
|
|
464
|
+
};
|
|
465
|
+
};
|
|
466
|
+
var validateSoul = (soul) => {
|
|
467
|
+
const errors = [];
|
|
468
|
+
if (!soul.id) errors.push("Missing id");
|
|
469
|
+
if (!soul.persona) errors.push("Missing persona");
|
|
470
|
+
if (!soul.state) errors.push("Missing state");
|
|
471
|
+
return {
|
|
472
|
+
valid: errors.length === 0,
|
|
473
|
+
errors
|
|
474
|
+
};
|
|
475
|
+
};
|
|
476
|
+
|
|
477
|
+
// src/ghost.ts
|
|
478
|
+
var startGhostSession = async (config) => {
|
|
479
|
+
const apiUrl = config.apiUrl || "https://api.forboc.ai";
|
|
480
|
+
try {
|
|
481
|
+
const response = await fetch(`${apiUrl}/ghost/run`, {
|
|
482
|
+
method: "POST",
|
|
483
|
+
headers: { "Content-Type": "application/json" },
|
|
484
|
+
body: JSON.stringify({
|
|
485
|
+
testSuite: config.testSuite,
|
|
486
|
+
duration: config.duration
|
|
487
|
+
})
|
|
488
|
+
});
|
|
489
|
+
if (!response.ok) {
|
|
490
|
+
throw new Error(`Failed to start Ghost session: ${response.statusText}`);
|
|
491
|
+
}
|
|
492
|
+
const data = await response.json();
|
|
493
|
+
return {
|
|
494
|
+
sessionId: data.sessionId,
|
|
495
|
+
status: data.runStatus
|
|
496
|
+
};
|
|
497
|
+
} catch (e) {
|
|
498
|
+
const mockId = `ghost_${Date.now()}_${Math.random().toString(36).substring(7)}`;
|
|
499
|
+
return {
|
|
500
|
+
sessionId: mockId,
|
|
501
|
+
status: "running"
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
};
|
|
505
|
+
var getGhostStatus = async (sessionId, apiUrl) => {
|
|
506
|
+
const url = apiUrl || "https://api.forboc.ai";
|
|
507
|
+
try {
|
|
508
|
+
const response = await fetch(`${url}/ghost/${sessionId}/status`, {
|
|
509
|
+
method: "GET",
|
|
510
|
+
headers: { "Content-Type": "application/json" }
|
|
511
|
+
});
|
|
512
|
+
if (!response.ok) {
|
|
513
|
+
throw new Error(`Failed to get Ghost status: ${response.statusText}`);
|
|
514
|
+
}
|
|
515
|
+
const data = await response.json();
|
|
516
|
+
return {
|
|
517
|
+
sessionId: data.ghostSessionId,
|
|
518
|
+
status: data.ghostStatus,
|
|
519
|
+
progress: data.ghostProgress,
|
|
520
|
+
startedAt: data.ghostStartedAt,
|
|
521
|
+
duration: data.ghostDuration,
|
|
522
|
+
errors: data.ghostErrors
|
|
523
|
+
};
|
|
524
|
+
} catch (e) {
|
|
525
|
+
return {
|
|
526
|
+
sessionId,
|
|
527
|
+
status: "running",
|
|
528
|
+
progress: Math.floor(Math.random() * 100),
|
|
529
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
530
|
+
duration: 60,
|
|
531
|
+
errors: 0
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
};
|
|
535
|
+
var getGhostResults = async (sessionId, apiUrl) => {
|
|
536
|
+
const url = apiUrl || "https://api.forboc.ai";
|
|
537
|
+
try {
|
|
538
|
+
const response = await fetch(`${url}/ghost/${sessionId}/results`, {
|
|
539
|
+
method: "GET",
|
|
540
|
+
headers: { "Content-Type": "application/json" }
|
|
541
|
+
});
|
|
542
|
+
if (!response.ok) {
|
|
543
|
+
throw new Error(`Failed to get Ghost results: ${response.statusText}`);
|
|
544
|
+
}
|
|
545
|
+
const data = await response.json();
|
|
546
|
+
return {
|
|
547
|
+
sessionId: data.resultsSessionId,
|
|
548
|
+
totalTests: data.resultsTotalTests,
|
|
549
|
+
passed: data.resultsPassed,
|
|
550
|
+
failed: data.resultsFailed,
|
|
551
|
+
skipped: data.resultsSkipped,
|
|
552
|
+
duration: data.resultsDuration,
|
|
553
|
+
tests: data.resultsTests.map((t) => ({
|
|
554
|
+
name: t.testName,
|
|
555
|
+
passed: t.testPassed,
|
|
556
|
+
duration: t.testDuration,
|
|
557
|
+
error: t.testError,
|
|
558
|
+
screenshot: t.testScreenshot
|
|
559
|
+
})),
|
|
560
|
+
coverage: data.resultsCoverage,
|
|
561
|
+
metrics: Object.fromEntries(data.resultsMetrics)
|
|
562
|
+
};
|
|
563
|
+
} catch (e) {
|
|
564
|
+
return {
|
|
565
|
+
sessionId,
|
|
566
|
+
totalTests: 5,
|
|
567
|
+
passed: 4,
|
|
568
|
+
failed: 1,
|
|
569
|
+
skipped: 0,
|
|
570
|
+
duration: 1245,
|
|
571
|
+
tests: [
|
|
572
|
+
{ name: "test.navigation", passed: true, duration: 150 },
|
|
573
|
+
{ name: "test.combat", passed: true, duration: 230 },
|
|
574
|
+
{ name: "test.dialogue", passed: false, duration: 450, error: "Timeout" }
|
|
575
|
+
],
|
|
576
|
+
coverage: 0.75,
|
|
577
|
+
metrics: {
|
|
578
|
+
avgFrameRate: 58.5,
|
|
579
|
+
memoryUsageMB: 512.3,
|
|
580
|
+
aiDecisionsPerSec: 15.2
|
|
581
|
+
}
|
|
582
|
+
};
|
|
583
|
+
}
|
|
584
|
+
};
|
|
585
|
+
var waitForGhostCompletion = async (sessionId, pollIntervalMs = 5e3, timeoutMs = 3e5, apiUrl, onProgress) => {
|
|
586
|
+
const startTime = Date.now();
|
|
587
|
+
while (Date.now() - startTime < timeoutMs) {
|
|
588
|
+
try {
|
|
589
|
+
const status = await getGhostStatus(sessionId, apiUrl);
|
|
590
|
+
if (onProgress) {
|
|
591
|
+
onProgress(status);
|
|
592
|
+
}
|
|
593
|
+
if (status.status === "completed") {
|
|
594
|
+
return getGhostResults(sessionId, apiUrl);
|
|
595
|
+
}
|
|
596
|
+
if (status.status === "failed") {
|
|
597
|
+
throw new Error(`Ghost session failed with ${status.errors} errors`);
|
|
598
|
+
}
|
|
599
|
+
} catch (e) {
|
|
600
|
+
}
|
|
601
|
+
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
|
|
602
|
+
}
|
|
603
|
+
throw new Error(`Ghost session timed out after ${timeoutMs}ms`);
|
|
604
|
+
};
|
|
605
|
+
var stopGhostSession = async (sessionId, apiUrl) => {
|
|
606
|
+
const url = apiUrl || "https://api.forboc.ai";
|
|
607
|
+
try {
|
|
608
|
+
const response = await fetch(`${url}/ghost/${sessionId}/stop`, {
|
|
609
|
+
method: "POST",
|
|
610
|
+
headers: { "Content-Type": "application/json" }
|
|
611
|
+
});
|
|
612
|
+
if (!response.ok) {
|
|
613
|
+
throw new Error(`Failed to stop Ghost session: ${response.statusText}`);
|
|
614
|
+
}
|
|
615
|
+
const data = await response.json();
|
|
616
|
+
return {
|
|
617
|
+
stopped: true,
|
|
618
|
+
status: data.status || "stopped"
|
|
619
|
+
};
|
|
620
|
+
} catch (e) {
|
|
621
|
+
return {
|
|
622
|
+
stopped: true,
|
|
623
|
+
status: "stopped"
|
|
624
|
+
};
|
|
625
|
+
}
|
|
626
|
+
};
|
|
627
|
+
var getGhostHistory = async (limit = 10, apiUrl) => {
|
|
628
|
+
const url = apiUrl || "https://api.forboc.ai";
|
|
629
|
+
try {
|
|
630
|
+
const response = await fetch(`${url}/ghost/history?limit=${limit}`, {
|
|
631
|
+
method: "GET",
|
|
632
|
+
headers: { "Content-Type": "application/json" }
|
|
633
|
+
});
|
|
634
|
+
if (!response.ok) {
|
|
635
|
+
throw new Error(`Failed to get Ghost history: ${response.statusText}`);
|
|
636
|
+
}
|
|
637
|
+
const data = await response.json();
|
|
638
|
+
return data.sessions.map((s) => ({
|
|
639
|
+
sessionId: s.sessionId,
|
|
640
|
+
testSuite: s.testSuite,
|
|
641
|
+
startedAt: s.startedAt,
|
|
642
|
+
completedAt: s.completedAt,
|
|
643
|
+
status: s.status,
|
|
644
|
+
passRate: s.passRate
|
|
645
|
+
}));
|
|
646
|
+
} catch (e) {
|
|
647
|
+
return [];
|
|
648
|
+
}
|
|
649
|
+
};
|
|
650
|
+
var createGhost = (config) => {
|
|
651
|
+
let sessionId = null;
|
|
652
|
+
const apiUrl = config.apiUrl || "https://api.forboc.ai";
|
|
653
|
+
const run = async () => {
|
|
654
|
+
const result = await startGhostSession(config);
|
|
655
|
+
sessionId = result.sessionId;
|
|
656
|
+
return sessionId;
|
|
657
|
+
};
|
|
658
|
+
const status = async () => {
|
|
659
|
+
if (!sessionId) {
|
|
660
|
+
throw new Error("Ghost session not started");
|
|
661
|
+
}
|
|
662
|
+
return getGhostStatus(sessionId, apiUrl);
|
|
663
|
+
};
|
|
664
|
+
const results = async () => {
|
|
665
|
+
if (!sessionId) {
|
|
666
|
+
throw new Error("Ghost session not started");
|
|
667
|
+
}
|
|
668
|
+
return getGhostResults(sessionId, apiUrl);
|
|
669
|
+
};
|
|
670
|
+
const stop = async () => {
|
|
671
|
+
sessionId = null;
|
|
672
|
+
};
|
|
673
|
+
const waitForCompletion = async (pollIntervalMs, timeoutMs, onProgress) => {
|
|
674
|
+
if (!sessionId) {
|
|
675
|
+
throw new Error("Ghost session not started");
|
|
676
|
+
}
|
|
677
|
+
return waitForGhostCompletion(
|
|
678
|
+
sessionId,
|
|
679
|
+
pollIntervalMs,
|
|
680
|
+
timeoutMs,
|
|
681
|
+
apiUrl,
|
|
682
|
+
onProgress
|
|
683
|
+
);
|
|
684
|
+
};
|
|
685
|
+
return {
|
|
686
|
+
run,
|
|
687
|
+
status,
|
|
688
|
+
results,
|
|
689
|
+
stop,
|
|
690
|
+
waitForCompletion
|
|
691
|
+
};
|
|
692
|
+
};
|
|
693
|
+
|
|
694
|
+
// src/cortex-remote.ts
|
|
695
|
+
var RemoteCortex = class {
|
|
696
|
+
constructor(apiUrl) {
|
|
697
|
+
this.apiUrl = apiUrl;
|
|
698
|
+
}
|
|
699
|
+
async init() {
|
|
700
|
+
return {
|
|
701
|
+
id: `remote_${Date.now()}`,
|
|
702
|
+
model: "api-integrated",
|
|
703
|
+
ready: true,
|
|
704
|
+
engine: "remote"
|
|
705
|
+
};
|
|
706
|
+
}
|
|
707
|
+
async complete(prompt, options) {
|
|
708
|
+
const response = await fetch(`${this.apiUrl}/cortex/complete`, {
|
|
709
|
+
method: "POST",
|
|
710
|
+
headers: { "Content-Type": "application/json" },
|
|
711
|
+
body: JSON.stringify({ prompt, ...options })
|
|
712
|
+
});
|
|
713
|
+
if (!response.ok) throw new Error(`Remote Cortex failed: ${response.statusText}`);
|
|
714
|
+
const data = await response.json();
|
|
715
|
+
return data.text;
|
|
716
|
+
}
|
|
717
|
+
async *completeStream(prompt, options) {
|
|
718
|
+
const result = await this.complete(prompt, options);
|
|
719
|
+
yield result;
|
|
720
|
+
}
|
|
721
|
+
};
|
|
722
|
+
|
|
723
|
+
// src/presets/index.ts
|
|
724
|
+
var presets_exports = {};
|
|
725
|
+
__export(presets_exports, {
|
|
726
|
+
RPG_MEMORY_TYPES: () => RPG_MEMORY_TYPES,
|
|
727
|
+
RPG_MOODS: () => RPG_MOODS,
|
|
728
|
+
attackRule: () => attackRule,
|
|
729
|
+
createRPGState: () => createRPGState,
|
|
730
|
+
interactRule: () => interactRule,
|
|
731
|
+
movementRule: () => movementRule,
|
|
732
|
+
puzzleRules: () => puzzleRules,
|
|
733
|
+
resourceRule: () => resourceRule,
|
|
734
|
+
rpgRules: () => rpgRules,
|
|
735
|
+
socialRules: () => socialRules,
|
|
736
|
+
spatialRules: () => spatialRules,
|
|
737
|
+
speakRule: () => speakRule
|
|
738
|
+
});
|
|
739
|
+
|
|
740
|
+
// src/presets/rpg.ts
|
|
741
|
+
var RPG_MOODS = {
|
|
742
|
+
hostile: "hostile",
|
|
743
|
+
suspicious: "suspicious",
|
|
744
|
+
neutral: "neutral",
|
|
745
|
+
friendly: "friendly",
|
|
746
|
+
loyal: "loyal"
|
|
747
|
+
};
|
|
748
|
+
var RPG_MEMORY_TYPES = {
|
|
749
|
+
observation: "observation",
|
|
750
|
+
experience: "experience",
|
|
751
|
+
knowledge: "knowledge",
|
|
752
|
+
emotion: "emotion"
|
|
753
|
+
};
|
|
754
|
+
var createRPGState = (partial) => {
|
|
755
|
+
return {
|
|
756
|
+
inventory: partial?.inventory ?? [],
|
|
757
|
+
hp: partial?.hp ?? 100,
|
|
758
|
+
mana: partial?.mana ?? 100,
|
|
759
|
+
skills: partial?.skills ?? {},
|
|
760
|
+
relationships: partial?.relationships ?? {},
|
|
761
|
+
mood: partial?.mood ?? "neutral",
|
|
762
|
+
...partial
|
|
763
|
+
};
|
|
764
|
+
};
|
|
765
|
+
var movementRule = {
|
|
766
|
+
id: "core.movement",
|
|
767
|
+
name: "Movement Validation",
|
|
768
|
+
description: "Ensures MOVE actions have valid x,y coordinates",
|
|
769
|
+
actionTypes: ["MOVE", "move"],
|
|
770
|
+
validate: (action, context) => {
|
|
771
|
+
const payload = action.payload || {};
|
|
772
|
+
const target = payload.target;
|
|
773
|
+
if (!target || typeof target.x !== "number" || typeof target.y !== "number") {
|
|
774
|
+
return {
|
|
775
|
+
valid: false,
|
|
776
|
+
reason: "MOVE action must have target with numeric x,y coordinates",
|
|
777
|
+
correctedAction: {
|
|
778
|
+
...action,
|
|
779
|
+
type: "IDLE",
|
|
780
|
+
reason: "Invalid MOVE target - defaulting to IDLE"
|
|
781
|
+
}
|
|
782
|
+
};
|
|
783
|
+
}
|
|
784
|
+
const world = context.worldState || {};
|
|
785
|
+
const maxX = world.maxX || Infinity;
|
|
786
|
+
const maxY = world.maxY || Infinity;
|
|
787
|
+
if (target.x < 0 || target.x > maxX || target.y < 0 || target.y > maxY) {
|
|
788
|
+
return {
|
|
789
|
+
valid: false,
|
|
790
|
+
reason: `MOVE target (${target.x}, ${target.y}) is out of bounds`,
|
|
791
|
+
correctedAction: {
|
|
792
|
+
...action,
|
|
793
|
+
payload: {
|
|
794
|
+
...payload,
|
|
795
|
+
target: {
|
|
796
|
+
x: Math.max(0, Math.min(maxX, target.x)),
|
|
797
|
+
y: Math.max(0, Math.min(maxY, target.y))
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
};
|
|
802
|
+
}
|
|
803
|
+
return { valid: true };
|
|
804
|
+
}
|
|
805
|
+
};
|
|
806
|
+
var attackRule = {
|
|
807
|
+
id: "core.attack",
|
|
808
|
+
name: "Attack Validation",
|
|
809
|
+
description: "Ensures ATTACK actions have valid targetId",
|
|
810
|
+
actionTypes: ["ATTACK", "attack"],
|
|
811
|
+
validate: (action, context) => {
|
|
812
|
+
if (!action.target && !action.payload?.targetId) {
|
|
813
|
+
return {
|
|
814
|
+
valid: false,
|
|
815
|
+
reason: "ATTACK action must have a target or targetId",
|
|
816
|
+
correctedAction: {
|
|
817
|
+
...action,
|
|
818
|
+
type: "IDLE",
|
|
819
|
+
reason: "Invalid ATTACK - no target specified"
|
|
820
|
+
}
|
|
821
|
+
};
|
|
822
|
+
}
|
|
823
|
+
const world = context.worldState || {};
|
|
824
|
+
const entities = world.entities || [];
|
|
825
|
+
const targetId = action.target || action.payload?.targetId;
|
|
826
|
+
if (entities.length > 0 && !entities.includes(targetId)) {
|
|
827
|
+
return {
|
|
828
|
+
valid: false,
|
|
829
|
+
reason: `ATTACK target '${targetId}' does not exist in world`,
|
|
830
|
+
correctedAction: {
|
|
831
|
+
...action,
|
|
832
|
+
type: "IDLE",
|
|
833
|
+
reason: "Target not found"
|
|
834
|
+
}
|
|
835
|
+
};
|
|
836
|
+
}
|
|
837
|
+
return { valid: true };
|
|
838
|
+
}
|
|
839
|
+
};
|
|
840
|
+
var interactRule = {
|
|
841
|
+
id: "core.interact",
|
|
842
|
+
name: "Interact Validation",
|
|
843
|
+
description: "Ensures INTERACT actions have valid objectId",
|
|
844
|
+
actionTypes: ["INTERACT", "interact"],
|
|
845
|
+
validate: (action, context) => {
|
|
846
|
+
if (!action.target && !action.payload?.objectId) {
|
|
847
|
+
return {
|
|
848
|
+
valid: false,
|
|
849
|
+
reason: "INTERACT action must have objectId",
|
|
850
|
+
correctedAction: {
|
|
851
|
+
...action,
|
|
852
|
+
type: "IDLE",
|
|
853
|
+
reason: "Invalid INTERACT - no object specified"
|
|
854
|
+
}
|
|
855
|
+
};
|
|
856
|
+
}
|
|
857
|
+
return { valid: true };
|
|
858
|
+
}
|
|
859
|
+
};
|
|
860
|
+
var speakRule = {
|
|
861
|
+
id: "core.speak",
|
|
862
|
+
name: "Speak Validation",
|
|
863
|
+
description: "Ensures SPEAK actions have non-empty text",
|
|
864
|
+
actionTypes: ["SPEAK", "speak"],
|
|
865
|
+
validate: (action, context) => {
|
|
866
|
+
const text = action.payload?.text;
|
|
867
|
+
if (!text || text.trim().length === 0) {
|
|
868
|
+
return {
|
|
869
|
+
valid: false,
|
|
870
|
+
reason: "SPEAK action must have non-empty text",
|
|
871
|
+
correctedAction: {
|
|
872
|
+
...action,
|
|
873
|
+
type: "IDLE",
|
|
874
|
+
reason: "Empty speech - defaulting to IDLE"
|
|
875
|
+
}
|
|
876
|
+
};
|
|
877
|
+
}
|
|
878
|
+
const maxLength = context.constraints?.maxSpeechLength || 1e3;
|
|
879
|
+
if (text.length > maxLength) {
|
|
880
|
+
return {
|
|
881
|
+
valid: true,
|
|
882
|
+
reason: `Speech truncated to ${maxLength} characters`,
|
|
883
|
+
correctedAction: {
|
|
884
|
+
...action,
|
|
885
|
+
payload: {
|
|
886
|
+
...action.payload,
|
|
887
|
+
text: text.substring(0, maxLength)
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
};
|
|
891
|
+
}
|
|
892
|
+
return { valid: true };
|
|
893
|
+
}
|
|
894
|
+
};
|
|
895
|
+
var resourceRule = {
|
|
896
|
+
id: "core.resources",
|
|
897
|
+
name: "Resource Validation",
|
|
898
|
+
description: "Ensures agent has required resources for action",
|
|
899
|
+
actionTypes: ["ATTACK", "CAST", "USE_ITEM"],
|
|
900
|
+
validate: (action, context) => {
|
|
901
|
+
const agent = context.agentState || {};
|
|
902
|
+
const hp = agent.hp ?? 100;
|
|
903
|
+
const mana = agent.mana ?? 100;
|
|
904
|
+
if (hp <= 0) {
|
|
905
|
+
return {
|
|
906
|
+
valid: false,
|
|
907
|
+
reason: "Agent is dead and cannot perform actions",
|
|
908
|
+
correctedAction: { ...action, type: "IDLE", reason: "Agent dead" }
|
|
909
|
+
};
|
|
910
|
+
}
|
|
911
|
+
if (action.type === "CAST" || action.type === "cast") {
|
|
912
|
+
const manaCost = action.payload?.manaCost || 10;
|
|
913
|
+
if (mana < manaCost) {
|
|
914
|
+
return {
|
|
915
|
+
valid: false,
|
|
916
|
+
reason: `Insufficient mana: need ${manaCost}, have ${mana}`,
|
|
917
|
+
correctedAction: { ...action, type: "IDLE", reason: "Not enough mana" }
|
|
918
|
+
};
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
return { valid: true };
|
|
922
|
+
}
|
|
923
|
+
};
|
|
924
|
+
var rpgRules = [movementRule, attackRule, interactRule, speakRule, resourceRule];
|
|
925
|
+
var spatialRules = [movementRule];
|
|
926
|
+
var socialRules = [speakRule, interactRule];
|
|
927
|
+
var puzzleRules = [movementRule, interactRule];
|
|
928
|
+
|
|
929
|
+
// src/index.ts
|
|
930
|
+
var SDK_VERSION = "0.4.4";
|
|
931
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
932
|
+
0 && (module.exports = {
|
|
933
|
+
RemoteCortex,
|
|
934
|
+
SDK_VERSION,
|
|
935
|
+
createAgent,
|
|
936
|
+
createBridge,
|
|
937
|
+
createGhost,
|
|
938
|
+
createInitialState,
|
|
939
|
+
createSoul,
|
|
940
|
+
createSoulInstance,
|
|
941
|
+
deserializeSoul,
|
|
942
|
+
exportSoul,
|
|
943
|
+
exportToSoul,
|
|
944
|
+
fromSoul,
|
|
945
|
+
getGhostHistory,
|
|
946
|
+
getGhostResults,
|
|
947
|
+
getGhostStatus,
|
|
948
|
+
getSoulList,
|
|
949
|
+
importSoulFromArweave,
|
|
950
|
+
presets,
|
|
951
|
+
processAgentInput,
|
|
952
|
+
serializeSoul,
|
|
953
|
+
startGhostSession,
|
|
954
|
+
stopGhostSession,
|
|
955
|
+
updateAgentState,
|
|
956
|
+
uploadToArweave,
|
|
957
|
+
validateAction,
|
|
958
|
+
validateSoul,
|
|
959
|
+
waitForGhostCompletion
|
|
960
|
+
});
|