@forbocai/core 0.5.6 → 0.5.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,145 +1,123 @@
1
1
  import {
2
- __export
3
- } from "./chunk-7P6ASYW6.mjs";
2
+ createSoul,
3
+ createSoulInstance,
4
+ deserializeSoul,
5
+ exportSoul,
6
+ getSoulList,
7
+ importSoulFromArweave,
8
+ serializeSoul,
9
+ validateSoul
10
+ } from "./chunk-M3F3ED7H.mjs";
4
11
 
5
12
  // src/agent.ts
6
- var createInitialState = (partial) => {
7
- return { ...partial };
8
- };
9
- var updateAgentState = (currentState, updates) => {
10
- return {
11
- ...currentState,
12
- ...updates
13
- };
14
- };
15
- var exportToSoul = (agentId, name, persona, state, memories) => {
16
- return {
17
- id: agentId,
18
- version: "1.0.0",
19
- name,
20
- persona,
21
- memories: [...memories],
22
- state: { ...state }
23
- };
13
+ var createInitialState = (partial) => ({ ...partial });
14
+ var updateAgentState = (currentState, updates) => ({
15
+ ...currentState,
16
+ ...updates
17
+ });
18
+ var exportToSoul = (agentId, name, persona, state, memories) => ({
19
+ id: agentId,
20
+ version: "1.0.0",
21
+ name,
22
+ persona,
23
+ memories: [...memories],
24
+ state: { ...state }
25
+ });
26
+ var storeMemoriesRecursive = async (memory, instructions, index = 0) => {
27
+ if (index >= instructions.length) return;
28
+ const { text, type, importance } = instructions[index];
29
+ await memory.store(text, type, importance).catch((e) => console.warn("Memory store failed:", e));
30
+ return storeMemoriesRecursive(memory, instructions, index + 1);
24
31
  };
25
32
  var createAgent = (config) => {
26
- let state = createInitialState(config.initialState);
33
+ let _state = createInitialState(config.initialState);
27
34
  const cortex = config.cortex;
28
35
  const apiUrl = config.apiUrl || "https://api.forboc.ai";
29
36
  const agentId = config.id || "agent-" + Math.random().toString(36).substring(7);
30
- const getAgentState = () => {
31
- return { ...state };
32
- };
33
- const setAgentState = (newState) => {
34
- state = newState;
37
+ const authHeaders = config.apiKey ? { "Content-Type": "application/json", "Authorization": `Bearer ${config.apiKey}` } : { "Content-Type": "application/json" };
38
+ const getState = () => ({ ..._state });
39
+ const setState = (newState) => {
40
+ _state = newState;
35
41
  };
36
42
  const process = async (input, context = {}) => {
37
- const currentState = getAgentState();
38
- const directiveBody = {
39
- observation: input,
40
- agentState: currentState,
41
- context
42
- };
43
+ const currentState = getState();
43
44
  const dirRes = await fetch(`${apiUrl}/agents/${agentId}/directive`, {
44
45
  method: "POST",
45
- headers: { "Content-Type": "application/json" },
46
- body: JSON.stringify(directiveBody)
46
+ headers: authHeaders,
47
+ body: JSON.stringify({ observation: input, agentState: currentState, context })
47
48
  });
48
- if (!dirRes.ok) {
49
- throw new Error(`API Directive Error: ${dirRes.status}`);
50
- }
49
+ if (!dirRes.ok) throw new Error(`API Directive Error: ${dirRes.status}`);
51
50
  const directiveData = await dirRes.json();
52
- let recalledMemories = [];
53
- if (config.memory && typeof config.memory.recall === "function") {
54
- try {
55
- const rawMemories = await config.memory.recall(
56
- directiveData.memoryRecall.query,
57
- directiveData.memoryRecall.limit,
58
- directiveData.memoryRecall.threshold
59
- );
60
- recalledMemories = rawMemories.map((m) => ({
61
- text: m.text,
62
- type: m.type,
63
- importance: m.importance,
64
- similarity: void 0
65
- }));
66
- } catch (e) {
67
- console.warn("Memory recall failed, continuing with empty memories:", e);
68
- }
69
- }
70
- const contextBody = {
71
- memories: recalledMemories,
72
- observation: input,
73
- agentState: currentState
74
- };
51
+ const recalledMemories = config.memory && directiveData.memoryRecall ? await config.memory.recall(
52
+ directiveData.memoryRecall.query,
53
+ directiveData.memoryRecall.limit,
54
+ directiveData.memoryRecall.threshold
55
+ ).then((mems) => mems.map((m) => ({ text: m.text, type: m.type, importance: m.importance, similarity: void 0 }))).catch(() => []) : [];
75
56
  const ctxRes = await fetch(`${apiUrl}/agents/${agentId}/context`, {
76
57
  method: "POST",
77
- headers: { "Content-Type": "application/json" },
78
- body: JSON.stringify(contextBody)
58
+ headers: authHeaders,
59
+ body: JSON.stringify({ memories: recalledMemories, observation: input, agentState: currentState })
79
60
  });
80
- if (!ctxRes.ok) {
81
- throw new Error(`API Context Error: ${ctxRes.status}`);
82
- }
61
+ if (!ctxRes.ok) throw new Error(`API Context Error: ${ctxRes.status}`);
83
62
  const contextData = await ctxRes.json();
84
63
  const generatedText = await cortex.complete(contextData.prompt, {
85
64
  maxTokens: contextData.constraints.maxTokens,
86
65
  temperature: contextData.constraints.temperature,
87
66
  stop: contextData.constraints.stop
88
67
  });
89
- const verdictBody = {
90
- generatedOutput: generatedText,
91
- observation: input,
92
- agentState: currentState
93
- };
94
68
  const verRes = await fetch(`${apiUrl}/agents/${agentId}/verdict`, {
95
69
  method: "POST",
96
- headers: { "Content-Type": "application/json" },
97
- body: JSON.stringify(verdictBody)
70
+ headers: authHeaders,
71
+ body: JSON.stringify({ generatedOutput: generatedText, observation: input, agentState: currentState })
98
72
  });
99
- if (!verRes.ok) {
100
- throw new Error(`API Verdict Error: ${verRes.status}`);
101
- }
73
+ if (!verRes.ok) throw new Error(`API Verdict Error: ${verRes.status}`);
102
74
  const verdictData = await verRes.json();
103
75
  if (!verdictData.valid) {
104
- return {
105
- dialogue: "... [Action Blocked by Protocol]",
106
- action: { type: "BLOCKED", reason: "API Validation Failed" }
107
- };
76
+ return { dialogue: "... [Blocked]", action: { type: "BLOCKED", reason: "Validation Failed" } };
108
77
  }
109
- if (config.memory && typeof config.memory.store === "function" && verdictData.memoryStore) {
110
- for (const instruction of verdictData.memoryStore) {
111
- config.memory.store(instruction.text, instruction.type, instruction.importance).catch((e) => console.warn("Memory store failed:", e));
112
- }
78
+ if (config.memory && verdictData.memoryStore) {
79
+ storeMemoriesRecursive(config.memory, verdictData.memoryStore);
113
80
  }
114
- if (verdictData.stateDelta && Object.keys(verdictData.stateDelta).length > 0) {
115
- state = updateAgentState(state, verdictData.stateDelta);
81
+ if (verdictData.stateDelta) {
82
+ setState(updateAgentState(_state, verdictData.stateDelta));
116
83
  }
117
84
  return {
118
85
  dialogue: generatedText,
119
- action: verdictData.action ? {
120
- ...verdictData.action,
121
- signature: verdictData.signature
122
- } : void 0,
86
+ action: verdictData.action ? { ...verdictData.action, signature: verdictData.signature } : void 0,
123
87
  thought: generatedText
124
88
  };
125
89
  };
90
+ const speak = async (message, context = {}) => {
91
+ const res = await fetch(`${apiUrl}/agents/${agentId}/speak`, {
92
+ method: "POST",
93
+ headers: authHeaders,
94
+ body: JSON.stringify({ speakMessage: message, speakContext: context, speakAgentState: getState() })
95
+ });
96
+ if (!res.ok) throw new Error(`API Speak Error: ${res.status}`);
97
+ const data = await res.json();
98
+ if (data.speakHistory) setState(updateAgentState(_state, { conversationHistory: data.speakHistory }));
99
+ return data.speakReply;
100
+ };
101
+ const dialogue = async (message, context = []) => {
102
+ const res = await fetch(`${apiUrl}/agents/${agentId}/dialogue`, {
103
+ method: "POST",
104
+ headers: authHeaders,
105
+ body: JSON.stringify({ diagMessage: message, diagContext: context })
106
+ });
107
+ if (!res.ok) throw new Error(`API Dialogue Error: ${res.status}`);
108
+ const data = await res.json();
109
+ return data.diagReply;
110
+ };
126
111
  const exportSoul2 = async () => {
127
- let exportedMemories = [];
128
- if (config.memory && typeof config.memory.export === "function") {
129
- try {
130
- exportedMemories = await config.memory.export();
131
- } catch (e) {
132
- console.warn("Memory export failed, exporting with empty memories:", e);
133
- }
134
- }
135
- return exportToSoul(agentId, "Agent", config.persona, state, exportedMemories);
112
+ const memories = config.memory ? await config.memory.export().catch(() => []) : [];
113
+ return exportToSoul(agentId, "Agent", config.persona, _state, memories);
136
114
  };
137
- return {
138
- process,
139
- getState: getAgentState,
140
- setState: setAgentState,
141
- export: exportSoul2
115
+ const exportSoulToArweave = async (soulConfig) => {
116
+ const soul = await exportSoul2();
117
+ const { exportSoul: exportFunc } = await import("./soul-QXKV5H2R.mjs");
118
+ return exportFunc(agentId, soul, { ...soulConfig, apiUrl });
142
119
  };
120
+ return { process, speak, dialogue, reply: speak, getState, setState, export: exportSoul2, exportSoul: exportSoulToArweave };
143
121
  };
144
122
  var fromSoul = async (soul, cortex, memory) => {
145
123
  const agent = createAgent({
@@ -149,412 +127,177 @@ var fromSoul = async (soul, cortex, memory) => {
149
127
  persona: soul.persona,
150
128
  initialState: soul.state
151
129
  });
152
- if (memory && soul.memories && soul.memories.length > 0 && typeof memory.import === "function") {
153
- try {
154
- await memory.import(soul.memories);
155
- } catch (e) {
156
- console.warn("Memory hydration failed, continuing without memories:", e);
157
- }
130
+ if (memory && soul.memories && soul.memories.length > 0) {
131
+ await memory.import(soul.memories).catch((e) => console.warn("Memory hydration failed:", e));
158
132
  }
159
133
  return agent;
160
134
  };
161
135
 
162
136
  // src/bridge.ts
163
137
  var createBridge = (config = {}) => {
164
- const effectiveConfig = {
165
- strictMode: config.strictMode ?? false,
166
- ...config
167
- };
168
- const rules = /* @__PURE__ */ new Map();
169
- if (config.customRules) {
170
- config.customRules.forEach((rule) => rules.set(rule.id, rule));
171
- }
172
- const validateRemote = async (action) => {
173
- const response = await fetch(`${effectiveConfig.apiUrl}/bridge/validate`, {
174
- method: "POST",
175
- headers: { "Content-Type": "application/json" },
176
- body: JSON.stringify({
177
- actionType: action.type,
178
- payload: JSON.stringify(action.payload || {})
179
- })
180
- });
181
- if (!response.ok) {
182
- throw new Error(`API validation failed: ${response.statusText}`);
183
- }
184
- const data = await response.json();
185
- return {
186
- valid: data.valid,
187
- reason: data.reason
188
- };
189
- };
138
+ const apiUrl = config.apiUrl || "https://api.forboc.ai";
139
+ const agentId = config.agentId;
190
140
  const validate = async (action, context = {}) => {
191
- const applicableRules = Array.from(rules.values()).filter(
192
- (rule) => rule.actionTypes.some(
193
- (t) => t.toLowerCase() === action.type.toLowerCase()
194
- )
195
- );
196
- if (effectiveConfig.strictMode && applicableRules.length === 0) {
197
- const safeTypes = ["IDLE", "idle", "WAIT", "wait"];
198
- if (!safeTypes.includes(action.type)) {
199
- return {
200
- valid: false,
201
- reason: `Unknown action type '${action.type}' in strict mode`,
202
- correctedAction: { ...action, type: "IDLE", reason: "Unknown action type" }
203
- };
204
- }
205
- }
206
- let currentAction = action;
207
- for (const rule of applicableRules) {
208
- const result = rule.validate(currentAction, context);
209
- if (!result.valid) {
210
- return result;
211
- }
212
- if (result.correctedAction) {
213
- currentAction = result.correctedAction;
141
+ const endpoint = agentId ? `${apiUrl}/bridge/validate/${agentId}` : `${apiUrl}/bridge/validate`;
142
+ try {
143
+ const response = await fetch(endpoint, {
144
+ method: "POST",
145
+ headers: { "Content-Type": "application/json" },
146
+ body: JSON.stringify({ action, context })
147
+ });
148
+ if (!response.ok) {
149
+ const error = await response.text();
150
+ throw new Error(`Bridge validation failed: ${error}`);
214
151
  }
152
+ const data = await response.json();
153
+ return data.brResult || data;
154
+ } catch (e) {
155
+ console.error("[Bridge] Validation error:", e);
156
+ return {
157
+ valid: false,
158
+ reason: `Network error reaching validation API: ${e instanceof Error ? e.message : String(e)}`
159
+ };
215
160
  }
216
- return {
217
- valid: true,
218
- correctedAction: currentAction !== action ? currentAction : void 0
219
- };
220
- };
221
- const registerRule = (rule) => {
222
- rules.set(rule.id, rule);
223
- };
224
- const listRules = () => {
225
- return Array.from(rules.values());
226
161
  };
227
- const removeRule = (ruleId) => {
228
- return rules.delete(ruleId);
229
- };
230
- return {
231
- validate,
232
- registerRule,
233
- listRules,
234
- removeRule
162
+ const listRulesets = async () => {
163
+ const response = await fetch(`${apiUrl}/rules`);
164
+ if (!response.ok) return [];
165
+ return response.json();
235
166
  };
167
+ return { validate, listRulesets };
236
168
  };
237
- var validateAction = (action, rules, context = {}) => {
238
- for (const rule of rules) {
239
- if (rule.actionTypes.some((t) => t.toLowerCase() === action.type.toLowerCase())) {
240
- const result = rule.validate(action, context);
241
- if (!result.valid) {
242
- return result;
243
- }
244
- }
169
+ var validateAction = async (action, context = {}, config = {}) => {
170
+ const bridge = createBridge(config);
171
+ return bridge.validate(action, context);
172
+ };
173
+ var loadPreset = async (presetName, apiUrl = "https://api.forboc.ai") => {
174
+ const response = await fetch(`${apiUrl}/rules/presets/${presetName}`, {
175
+ method: "POST"
176
+ });
177
+ if (!response.ok) {
178
+ throw new Error(`Failed to load preset '${presetName}': ${response.statusText}`);
245
179
  }
246
- return { valid: true };
180
+ return response.json();
247
181
  };
248
182
 
249
- // src/soul.ts
250
- var createSoul = (id, name, persona, state, memories = []) => {
251
- return {
252
- id,
253
- version: "1.0.0",
254
- name,
255
- persona,
256
- state: { ...state },
257
- memories: [...memories]
183
+ // src/utils.ts
184
+ var delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
185
+ var pipe = (...fns) => (initialValue) => fns.reduce((acc, fn) => fn(acc), initialValue);
186
+ var memoiseAsync = (fn) => {
187
+ let cached = null;
188
+ let promise = null;
189
+ return () => {
190
+ if (cached) return Promise.resolve(cached);
191
+ if (promise) return promise;
192
+ promise = fn().then((value) => {
193
+ cached = value;
194
+ return value;
195
+ });
196
+ return promise;
258
197
  };
259
198
  };
260
- var serializeSoul = (soul) => {
261
- return JSON.stringify(soul, null, 2);
199
+ var memoise = (fn) => {
200
+ let cached = null;
201
+ let initialized = false;
202
+ return () => {
203
+ if (initialized) return cached;
204
+ cached = fn();
205
+ initialized = true;
206
+ return cached;
207
+ };
262
208
  };
263
- var deserializeSoul = (json) => {
264
- const parsed = JSON.parse(json);
265
- if (!parsed.id || !parsed.persona || !parsed.state) {
266
- throw new Error("Invalid Soul format: missing required fields");
267
- }
209
+
210
+ // src/ghost.ts
211
+ var DEFAULT_API_URL = "https://api.forboc.ai";
212
+ var startGhostSession = async (config) => {
213
+ const apiUrl = config.apiUrl || DEFAULT_API_URL;
214
+ const response = await fetch(`${apiUrl}/ghost/run`, {
215
+ method: "POST",
216
+ headers: { "Content-Type": "application/json" },
217
+ body: JSON.stringify({
218
+ testSuite: config.testSuite,
219
+ duration: config.duration
220
+ })
221
+ });
222
+ if (!response.ok) throw new Error(`Failed to start Ghost: ${response.statusText}`);
223
+ const data = await response.json();
268
224
  return {
269
- id: parsed.id,
270
- version: parsed.version || "1.0.0",
271
- name: parsed.name || "Unknown",
272
- persona: parsed.persona,
273
- state: parsed.state,
274
- memories: parsed.memories || [],
275
- signature: parsed.signature
225
+ sessionId: data.sessionId,
226
+ status: data.runStatus
276
227
  };
277
228
  };
278
- var uploadToArweave = async (soul, config) => {
279
- const wallet = config.walletJwk || config.privateKey;
280
- if (!wallet) {
281
- throw new Error("Arweave wallet JWK required for direct upload");
282
- }
283
- let createArweaveDataItem;
284
- try {
285
- const imported = await import("./ans104-GLQVFKBA.mjs");
286
- createArweaveDataItem = imported.createArweaveDataItem;
287
- } catch (e) {
288
- throw new Error("Missing ANS-104 Arweave module. Rebuild the SDK to enable direct uploads.");
289
- }
290
- const jwk = typeof wallet === "string" ? JSON.parse(wallet) : wallet;
291
- const dataToUpload = serializeSoul(soul);
292
- const tags = [
293
- { name: "Content-Type", value: "application/json" },
294
- { name: "App-Name", value: "ForbocAI" },
295
- { name: "App-Version", value: "soul-1" }
296
- ];
297
- const dataItem = await createArweaveDataItem(dataToUpload, jwk, { tags });
298
- const rawData = dataItem.raw;
299
- if (!rawData) {
300
- throw new Error("Failed to build ANS-104 data item");
301
- }
302
- const bundlerUrl = config.bundlerUrl || "https://upload.ardrive.io/v1/tx";
303
- const gatewayUrl = config.gatewayUrl || "https://arweave.net";
304
- try {
305
- const response = await fetch(bundlerUrl, {
306
- method: "POST",
307
- headers: { "Content-Type": "application/octet-stream" },
308
- body: rawData
309
- });
310
- if (!response.ok) {
311
- const message = await response.text().catch(() => response.statusText);
312
- throw new Error(message || response.statusText);
313
- }
314
- const responseText = await response.text();
315
- let responseJson = null;
316
- try {
317
- responseJson = JSON.parse(responseText);
318
- } catch {
319
- responseJson = null;
320
- }
321
- const txId = responseJson?.id || responseJson?.dataItemId || responseJson?.txId || (typeof responseText === "string" && responseText.trim().length > 0 ? responseText.trim() : null) || dataItem.id;
322
- if (!txId) {
323
- throw new Error("Bundler response did not include a data item id");
324
- }
325
- return {
326
- txId,
327
- url: `${gatewayUrl}/${txId}`,
328
- soul
329
- };
330
- } catch (e) {
331
- throw new Error(`Failed to upload to Arweave: ${e}`);
332
- }
333
- };
334
- var exportSoul = async (_agentId, soul, config = {}) => {
335
- if (!config.walletJwk && !config.privateKey) {
336
- throw new Error("walletJwk required for Arweave ANS-104 export");
337
- }
338
- return uploadToArweave(soul, config);
339
- };
340
- var importSoulFromArweave = async (txId, config = {}) => {
341
- const gateway = config.gatewayUrl || "https://arweave.net";
342
- try {
343
- const response = await fetch(`${gateway}/${txId}`, {
344
- method: "GET",
345
- headers: { "Content-Type": "application/json" }
346
- });
347
- if (!response.ok) {
348
- throw new Error(`Import failed: ${response.statusText}`);
349
- }
350
- const data = await response.json();
351
- if (!data.id || !data.persona) {
352
- throw new Error("Invalid soul data from Arweave");
353
- }
354
- return data;
355
- } catch (e) {
356
- throw new Error(`Failed to import Soul from TXID ${txId}: ${e}`);
357
- }
358
- };
359
- var getSoulList = async (limit = 50, apiUrl) => {
360
- const url = apiUrl || "https://api.forboc.ai";
361
- try {
362
- const response = await fetch(`${url}/souls?limit=${limit}`);
363
- if (!response.ok) throw new Error(response.statusText);
364
- const data = await response.json();
365
- return data.souls.map((s) => {
366
- const txId = s.txId || s.soulEntryTxId || s.cid;
367
- const name = s.name || s.soulEntryName;
368
- const agentId = s.agentId || s.soulEntryAgentId;
369
- const exportedAt = s.exportedAt || s.soulEntryExportedAt;
370
- const url2 = s.url || s.soulEntryArweaveUrl || `https://arweave.net/${txId}`;
371
- return { txId, name, agentId, exportedAt, url: url2 };
372
- });
373
- } catch (e) {
374
- return [];
375
- }
376
- };
377
- var createSoulInstance = (id, name, persona, state, memories = []) => {
378
- const soulData = createSoul(id, name, persona, state, memories);
379
- const performExport = async (config) => {
380
- return exportSoul(soulData.id, soulData, { ...config });
381
- };
229
+ var getGhostStatus = async (sessionId, apiUrl) => {
230
+ const url = apiUrl || DEFAULT_API_URL;
231
+ const response = await fetch(`${url}/ghost/${sessionId}/status`, {
232
+ method: "GET",
233
+ headers: { "Content-Type": "application/json" }
234
+ });
235
+ if (!response.ok) throw new Error(`Failed to get status: ${response.statusText}`);
236
+ const data = await response.json();
382
237
  return {
383
- export: performExport,
384
- toJSON: () => ({ ...soulData })
238
+ sessionId: data.ghostSessionId,
239
+ status: data.ghostStatus,
240
+ progress: data.ghostProgress,
241
+ startedAt: data.ghostStartedAt,
242
+ duration: data.ghostDuration,
243
+ errors: data.ghostErrors
385
244
  };
386
245
  };
387
- var validateSoul = (soul) => {
388
- const errors = [];
389
- if (!soul.id) errors.push("Missing id");
390
- if (!soul.persona) errors.push("Missing persona");
391
- if (!soul.state) errors.push("Missing state");
246
+ var getGhostResults = async (sessionId, apiUrl) => {
247
+ const url = apiUrl || DEFAULT_API_URL;
248
+ const response = await fetch(`${url}/ghost/${sessionId}/results`, {
249
+ method: "GET",
250
+ headers: { "Content-Type": "application/json" }
251
+ });
252
+ if (!response.ok) throw new Error(`Failed to get results: ${response.statusText}`);
253
+ const data = await response.json();
392
254
  return {
393
- valid: errors.length === 0,
394
- errors
255
+ sessionId: data.resultsSessionId,
256
+ totalTests: data.resultsTotalTests,
257
+ passed: data.resultsPassed,
258
+ failed: data.resultsFailed,
259
+ skipped: data.resultsSkipped,
260
+ duration: data.resultsDuration,
261
+ tests: data.resultsTests.map((t) => ({
262
+ name: t.testName,
263
+ passed: t.testPassed,
264
+ duration: t.testDuration,
265
+ error: t.testError,
266
+ screenshot: t.testScreenshot
267
+ })),
268
+ coverage: data.resultsCoverage,
269
+ metrics: Object.fromEntries(data.resultsMetrics || [])
395
270
  };
396
271
  };
397
-
398
- // src/ghost.ts
399
- var startGhostSession = async (config) => {
400
- const apiUrl = config.apiUrl || "https://api.forboc.ai";
401
- try {
402
- const response = await fetch(`${apiUrl}/ghost/run`, {
403
- method: "POST",
404
- headers: { "Content-Type": "application/json" },
405
- body: JSON.stringify({
406
- testSuite: config.testSuite,
407
- duration: config.duration
408
- })
409
- });
410
- if (!response.ok) {
411
- throw new Error(`Failed to start Ghost session: ${response.statusText}`);
412
- }
413
- const data = await response.json();
414
- return {
415
- sessionId: data.sessionId,
416
- status: data.runStatus
417
- };
418
- } catch (e) {
419
- const mockId = `ghost_${Date.now()}_${Math.random().toString(36).substring(7)}`;
420
- return {
421
- sessionId: mockId,
422
- status: "running"
423
- };
272
+ var pollForCompletion = async (sessionId, pollIntervalMs, timeoutMs, startTime, apiUrl, onProgress) => {
273
+ if (Date.now() - startTime > timeoutMs) {
274
+ throw new Error(`Ghost session timed out after ${timeoutMs}ms`);
424
275
  }
425
- };
426
- var getGhostStatus = async (sessionId, apiUrl) => {
427
- const url = apiUrl || "https://api.forboc.ai";
428
276
  try {
429
- const response = await fetch(`${url}/ghost/${sessionId}/status`, {
430
- method: "GET",
431
- headers: { "Content-Type": "application/json" }
432
- });
433
- if (!response.ok) {
434
- throw new Error(`Failed to get Ghost status: ${response.statusText}`);
435
- }
436
- const data = await response.json();
437
- return {
438
- sessionId: data.ghostSessionId,
439
- status: data.ghostStatus,
440
- progress: data.ghostProgress,
441
- startedAt: data.ghostStartedAt,
442
- duration: data.ghostDuration,
443
- errors: data.ghostErrors
444
- };
277
+ const status = await getGhostStatus(sessionId, apiUrl);
278
+ if (onProgress) onProgress(status);
279
+ if (status.status === "completed") return getGhostResults(sessionId, apiUrl);
280
+ if (status.status === "failed") throw new Error(`Ghost session failed with ${status.errors} errors`);
445
281
  } catch (e) {
446
- return {
447
- sessionId,
448
- status: "running",
449
- progress: Math.floor(Math.random() * 100),
450
- startedAt: (/* @__PURE__ */ new Date()).toISOString(),
451
- duration: 60,
452
- errors: 0
453
- };
454
282
  }
283
+ await delay(pollIntervalMs);
284
+ return pollForCompletion(sessionId, pollIntervalMs, timeoutMs, startTime, apiUrl, onProgress);
455
285
  };
456
- var getGhostResults = async (sessionId, apiUrl) => {
457
- const url = apiUrl || "https://api.forboc.ai";
458
- try {
459
- const response = await fetch(`${url}/ghost/${sessionId}/results`, {
460
- method: "GET",
461
- headers: { "Content-Type": "application/json" }
462
- });
463
- if (!response.ok) {
464
- throw new Error(`Failed to get Ghost results: ${response.statusText}`);
465
- }
466
- const data = await response.json();
467
- return {
468
- sessionId: data.resultsSessionId,
469
- totalTests: data.resultsTotalTests,
470
- passed: data.resultsPassed,
471
- failed: data.resultsFailed,
472
- skipped: data.resultsSkipped,
473
- duration: data.resultsDuration,
474
- tests: data.resultsTests.map((t) => ({
475
- name: t.testName,
476
- passed: t.testPassed,
477
- duration: t.testDuration,
478
- error: t.testError,
479
- screenshot: t.testScreenshot
480
- })),
481
- coverage: data.resultsCoverage,
482
- metrics: Object.fromEntries(data.resultsMetrics)
483
- };
484
- } catch (e) {
485
- return {
486
- sessionId,
487
- totalTests: 5,
488
- passed: 4,
489
- failed: 1,
490
- skipped: 0,
491
- duration: 1245,
492
- tests: [
493
- { name: "test.navigation", passed: true, duration: 150 },
494
- { name: "test.combat", passed: true, duration: 230 },
495
- { name: "test.dialogue", passed: false, duration: 450, error: "Timeout" }
496
- ],
497
- coverage: 0.75,
498
- metrics: {
499
- avgFrameRate: 58.5,
500
- memoryUsageMB: 512.3,
501
- aiDecisionsPerSec: 15.2
502
- }
503
- };
504
- }
505
- };
506
- var waitForGhostCompletion = async (sessionId, pollIntervalMs = 5e3, timeoutMs = 3e5, apiUrl, onProgress) => {
507
- const startTime = Date.now();
508
- while (Date.now() - startTime < timeoutMs) {
509
- try {
510
- const status = await getGhostStatus(sessionId, apiUrl);
511
- if (onProgress) {
512
- onProgress(status);
513
- }
514
- if (status.status === "completed") {
515
- return getGhostResults(sessionId, apiUrl);
516
- }
517
- if (status.status === "failed") {
518
- throw new Error(`Ghost session failed with ${status.errors} errors`);
519
- }
520
- } catch (e) {
521
- }
522
- await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
523
- }
524
- throw new Error(`Ghost session timed out after ${timeoutMs}ms`);
525
- };
286
+ var waitForGhostCompletion = (sessionId, pollIntervalMs = 5e3, timeoutMs = 3e5, apiUrl, onProgress) => pollForCompletion(sessionId, pollIntervalMs, timeoutMs, Date.now(), apiUrl, onProgress);
526
287
  var stopGhostSession = async (sessionId, apiUrl) => {
527
- const url = apiUrl || "https://api.forboc.ai";
288
+ const url = apiUrl || DEFAULT_API_URL;
528
289
  try {
529
- const response = await fetch(`${url}/ghost/${sessionId}/stop`, {
530
- method: "POST",
531
- headers: { "Content-Type": "application/json" }
532
- });
533
- if (!response.ok) {
534
- throw new Error(`Failed to stop Ghost session: ${response.statusText}`);
535
- }
536
- const data = await response.json();
537
- return {
538
- stopped: true,
539
- status: data.status || "stopped"
540
- };
290
+ const response = await fetch(`${url}/ghost/${sessionId}/stop`, { method: "POST" });
291
+ return { stopped: response.ok };
541
292
  } catch (e) {
542
- return {
543
- stopped: true,
544
- status: "stopped"
545
- };
293
+ return { stopped: true };
546
294
  }
547
295
  };
548
296
  var getGhostHistory = async (limit = 10, apiUrl) => {
549
- const url = apiUrl || "https://api.forboc.ai";
297
+ const url = apiUrl || DEFAULT_API_URL;
550
298
  try {
551
- const response = await fetch(`${url}/ghost/history?limit=${limit}`, {
552
- method: "GET",
553
- headers: { "Content-Type": "application/json" }
554
- });
555
- if (!response.ok) {
556
- throw new Error(`Failed to get Ghost history: ${response.statusText}`);
557
- }
299
+ const response = await fetch(`${url}/ghost/history?limit=${limit}`);
300
+ if (!response.ok) return [];
558
301
  const data = await response.json();
559
302
  return data.sessions.map((s) => ({
560
303
  sessionId: s.sessionId,
@@ -569,54 +312,33 @@ var getGhostHistory = async (limit = 10, apiUrl) => {
569
312
  }
570
313
  };
571
314
  var createGhost = (config) => {
572
- let sessionId = null;
573
- const apiUrl = config.apiUrl || "https://api.forboc.ai";
315
+ let _sessionId = null;
316
+ const apiUrl = config.apiUrl || DEFAULT_API_URL;
574
317
  const run = async () => {
575
318
  const result = await startGhostSession(config);
576
- sessionId = result.sessionId;
577
- return sessionId;
319
+ _sessionId = result.sessionId;
320
+ return _sessionId;
578
321
  };
579
- const status = async () => {
580
- if (!sessionId) {
581
- throw new Error("Ghost session not started");
582
- }
583
- return getGhostStatus(sessionId, apiUrl);
584
- };
585
- const results = async () => {
586
- if (!sessionId) {
587
- throw new Error("Ghost session not started");
588
- }
589
- return getGhostResults(sessionId, apiUrl);
322
+ const ensureSessionId = () => {
323
+ if (!_sessionId) throw new Error("Ghost session not started");
324
+ return _sessionId;
590
325
  };
326
+ const status = async () => getGhostStatus(ensureSessionId(), apiUrl);
327
+ const results = async () => getGhostResults(ensureSessionId(), apiUrl);
591
328
  const stop = async () => {
592
- if (sessionId) {
593
- await stopGhostSession(sessionId, apiUrl);
594
- }
595
- sessionId = null;
596
- };
597
- const waitForCompletion = async (pollIntervalMs, timeoutMs, onProgress) => {
598
- if (!sessionId) {
599
- throw new Error("Ghost session not started");
600
- }
601
- return waitForGhostCompletion(
602
- sessionId,
603
- pollIntervalMs,
604
- timeoutMs,
605
- apiUrl,
606
- onProgress
607
- );
608
- };
609
- return {
610
- run,
611
- status,
612
- results,
613
- stop,
614
- waitForCompletion
329
+ if (_sessionId) await stopGhostSession(_sessionId, apiUrl);
330
+ _sessionId = null;
615
331
  };
332
+ const waitForCompletion = (pollIntervalMs, timeoutMs, onProgress) => waitForGhostCompletion(ensureSessionId(), pollIntervalMs, timeoutMs, apiUrl, onProgress);
333
+ return { run, status, results, stop, waitForCompletion };
616
334
  };
617
335
 
618
336
  // src/cortex-remote.ts
619
- var createRemoteCortex = (apiUrl, cortexId = "local") => {
337
+ var createRemoteCortex = (apiUrl, cortexId = "local", apiKey) => {
338
+ const authHeaders = {
339
+ "Content-Type": "application/json",
340
+ ...apiKey ? { "Authorization": `Bearer ${apiKey}` } : {}
341
+ };
620
342
  const init = async () => ({
621
343
  id: `remote_${Date.now()}`,
622
344
  model: "api-integrated",
@@ -626,7 +348,7 @@ var createRemoteCortex = (apiUrl, cortexId = "local") => {
626
348
  const complete = async (prompt, options) => {
627
349
  const response = await fetch(`${apiUrl}/cortex/${cortexId}/complete`, {
628
350
  method: "POST",
629
- headers: { "Content-Type": "application/json" },
351
+ headers: authHeaders,
630
352
  body: JSON.stringify({ prompt, ...options })
631
353
  });
632
354
  if (!response.ok) throw new Error(`Remote Cortex failed: ${response.statusText}`);
@@ -640,214 +362,32 @@ var createRemoteCortex = (apiUrl, cortexId = "local") => {
640
362
  return { init, complete, completeStream };
641
363
  };
642
364
 
643
- // src/presets/index.ts
644
- var presets_exports = {};
645
- __export(presets_exports, {
646
- RPG_MEMORY_TYPES: () => RPG_MEMORY_TYPES,
647
- RPG_MOODS: () => RPG_MOODS,
648
- attackRule: () => attackRule,
649
- createRPGState: () => createRPGState,
650
- interactRule: () => interactRule,
651
- movementRule: () => movementRule,
652
- puzzleRules: () => puzzleRules,
653
- resourceRule: () => resourceRule,
654
- rpgRules: () => rpgRules,
655
- socialRules: () => socialRules,
656
- spatialRules: () => spatialRules,
657
- speakRule: () => speakRule
658
- });
659
-
660
- // src/presets/rpg.ts
661
- var RPG_MOODS = {
662
- hostile: "hostile",
663
- suspicious: "suspicious",
664
- neutral: "neutral",
665
- friendly: "friendly",
666
- loyal: "loyal"
667
- };
668
- var RPG_MEMORY_TYPES = {
669
- observation: "observation",
670
- experience: "experience",
671
- knowledge: "knowledge",
672
- emotion: "emotion"
673
- };
674
- var createRPGState = (partial) => {
675
- return {
676
- inventory: partial?.inventory ?? [],
677
- hp: partial?.hp ?? 100,
678
- mana: partial?.mana ?? 100,
679
- skills: partial?.skills ?? {},
680
- relationships: partial?.relationships ?? {},
681
- mood: partial?.mood ?? "neutral",
682
- ...partial
683
- };
684
- };
685
- var movementRule = {
686
- id: "core.movement",
687
- name: "Movement Validation",
688
- description: "Ensures MOVE actions have valid x,y coordinates",
689
- actionTypes: ["MOVE", "move"],
690
- validate: (action, context) => {
691
- const payload = action.payload || {};
692
- const target = payload.target;
693
- if (!target || typeof target.x !== "number" || typeof target.y !== "number") {
694
- return {
695
- valid: false,
696
- reason: "MOVE action must have target with numeric x,y coordinates",
697
- correctedAction: {
698
- ...action,
699
- type: "IDLE",
700
- reason: "Invalid MOVE target - defaulting to IDLE"
701
- }
702
- };
703
- }
704
- const world = context.worldState || {};
705
- const maxX = world.maxX || Infinity;
706
- const maxY = world.maxY || Infinity;
707
- if (target.x < 0 || target.x > maxX || target.y < 0 || target.y > maxY) {
708
- return {
709
- valid: false,
710
- reason: `MOVE target (${target.x}, ${target.y}) is out of bounds`,
711
- correctedAction: {
712
- ...action,
713
- payload: {
714
- ...payload,
715
- target: {
716
- x: Math.max(0, Math.min(maxX, target.x)),
717
- y: Math.max(0, Math.min(maxY, target.y))
718
- }
719
- }
720
- }
721
- };
722
- }
723
- return { valid: true };
724
- }
725
- };
726
- var attackRule = {
727
- id: "core.attack",
728
- name: "Attack Validation",
729
- description: "Ensures ATTACK actions have valid targetId",
730
- actionTypes: ["ATTACK", "attack"],
731
- validate: (action, context) => {
732
- if (!action.target && !action.payload?.targetId) {
733
- return {
734
- valid: false,
735
- reason: "ATTACK action must have a target or targetId",
736
- correctedAction: {
737
- ...action,
738
- type: "IDLE",
739
- reason: "Invalid ATTACK - no target specified"
740
- }
741
- };
742
- }
743
- const world = context.worldState || {};
744
- const entities = world.entities || [];
745
- const targetId = action.target || action.payload?.targetId;
746
- if (entities.length > 0 && !entities.includes(targetId)) {
747
- return {
748
- valid: false,
749
- reason: `ATTACK target '${targetId}' does not exist in world`,
750
- correctedAction: {
751
- ...action,
752
- type: "IDLE",
753
- reason: "Target not found"
754
- }
755
- };
756
- }
757
- return { valid: true };
758
- }
759
- };
760
- var interactRule = {
761
- id: "core.interact",
762
- name: "Interact Validation",
763
- description: "Ensures INTERACT actions have valid objectId",
764
- actionTypes: ["INTERACT", "interact"],
765
- validate: (action, context) => {
766
- if (!action.target && !action.payload?.objectId) {
767
- return {
768
- valid: false,
769
- reason: "INTERACT action must have objectId",
770
- correctedAction: {
771
- ...action,
772
- type: "IDLE",
773
- reason: "Invalid INTERACT - no object specified"
774
- }
775
- };
776
- }
777
- return { valid: true };
778
- }
365
+ // src/stream.ts
366
+ var consumeStream = async (stream, onChunk, acc = "", delayMs = 0) => {
367
+ const { value, done } = await stream.next();
368
+ if (done) return acc;
369
+ const chunk = value ?? "";
370
+ onChunk(chunk);
371
+ if (delayMs > 0) await delay(delayMs);
372
+ return consumeStream(stream, onChunk, acc + chunk, delayMs);
373
+ };
374
+ var streamToCallback = async (stream, onChunk) => {
375
+ await consumeStream(stream, onChunk);
376
+ };
377
+ var streamToString = async (stream) => {
378
+ return consumeStream(stream, () => {
379
+ });
779
380
  };
780
- var speakRule = {
781
- id: "core.speak",
782
- name: "Speak Validation",
783
- description: "Ensures SPEAK actions have non-empty text",
784
- actionTypes: ["SPEAK", "speak"],
785
- validate: (action, context) => {
786
- const text = action.payload?.text;
787
- if (!text || text.trim().length === 0) {
788
- return {
789
- valid: false,
790
- reason: "SPEAK action must have non-empty text",
791
- correctedAction: {
792
- ...action,
793
- type: "IDLE",
794
- reason: "Empty speech - defaulting to IDLE"
795
- }
796
- };
797
- }
798
- const maxLength = context.constraints?.maxSpeechLength || 1e3;
799
- if (text.length > maxLength) {
800
- return {
801
- valid: true,
802
- reason: `Speech truncated to ${maxLength} characters`,
803
- correctedAction: {
804
- ...action,
805
- payload: {
806
- ...action.payload,
807
- text: text.substring(0, maxLength)
808
- }
809
- }
810
- };
811
- }
812
- return { valid: true };
813
- }
381
+ var streamFromCortex = async (cortex, prompt, onChunk, options) => {
382
+ return consumeStream(cortex.completeStream(prompt, options), onChunk);
814
383
  };
815
- var resourceRule = {
816
- id: "core.resources",
817
- name: "Resource Validation",
818
- description: "Ensures agent has required resources for action",
819
- actionTypes: ["ATTACK", "CAST", "USE_ITEM"],
820
- validate: (action, context) => {
821
- const agent = context.agentState || {};
822
- const hp = agent.hp ?? 100;
823
- const mana = agent.mana ?? 100;
824
- if (hp <= 0) {
825
- return {
826
- valid: false,
827
- reason: "Agent is dead and cannot perform actions",
828
- correctedAction: { ...action, type: "IDLE", reason: "Agent dead" }
829
- };
830
- }
831
- if (action.type === "CAST" || action.type === "cast") {
832
- const manaCost = action.payload?.manaCost || 10;
833
- if (mana < manaCost) {
834
- return {
835
- valid: false,
836
- reason: `Insufficient mana: need ${manaCost}, have ${mana}`,
837
- correctedAction: { ...action, type: "IDLE", reason: "Not enough mana" }
838
- };
839
- }
840
- }
841
- return { valid: true };
842
- }
384
+ var streamFromCortexWithDelay = async (cortex, prompt, onChunk, options) => {
385
+ const { delayMs = 30, ...completionOptions } = options ?? {};
386
+ return consumeStream(cortex.completeStream(prompt, completionOptions), onChunk, "", delayMs);
843
387
  };
844
- var rpgRules = [movementRule, attackRule, interactRule, speakRule, resourceRule];
845
- var spatialRules = [movementRule];
846
- var socialRules = [speakRule, interactRule];
847
- var puzzleRules = [movementRule, interactRule];
848
388
 
849
389
  // src/index.ts
850
- var SDK_VERSION = "0.5.6";
390
+ var SDK_VERSION = "0.5.7";
851
391
  export {
852
392
  SDK_VERSION,
853
393
  createAgent,
@@ -857,6 +397,7 @@ export {
857
397
  createRemoteCortex,
858
398
  createSoul,
859
399
  createSoulInstance,
400
+ delay,
860
401
  deserializeSoul,
861
402
  exportSoul,
862
403
  exportToSoul,
@@ -866,12 +407,18 @@ export {
866
407
  getGhostStatus,
867
408
  getSoulList,
868
409
  importSoulFromArweave,
869
- presets_exports as presets,
410
+ loadPreset,
411
+ memoise,
412
+ memoiseAsync,
413
+ pipe,
870
414
  serializeSoul,
871
415
  startGhostSession,
872
416
  stopGhostSession,
417
+ streamFromCortex,
418
+ streamFromCortexWithDelay,
419
+ streamToCallback,
420
+ streamToString,
873
421
  updateAgentState,
874
- uploadToArweave,
875
422
  validateAction,
876
423
  validateSoul,
877
424
  waitForGhostCompletion