@forbocai/core 0.5.9 → 0.6.0

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,186 +1,515 @@
1
- import {
2
- createSoul,
3
- createSoulInstance,
4
- deserializeSoul,
5
- exportSoul,
6
- getSoulList,
7
- importSoulFromArweave,
8
- serializeSoul,
9
- validateSoul
10
- } from "./chunk-M3F3ED7H.mjs";
11
-
12
- // src/agent.ts
1
+ // src/npc.ts
13
2
  var createInitialState = (partial) => ({ ...partial });
14
- var updateAgentState = (currentState, updates) => ({
3
+ var updateNPCStateLocally = (currentState, updates) => ({
15
4
  ...currentState,
16
5
  ...updates
17
6
  });
18
- var exportToSoul = (agentId, name, persona, state, memories) => ({
19
- id: agentId,
7
+ var exportToSoul = (npcId, name, persona, state, memories) => ({
8
+ id: npcId,
20
9
  version: "1.0.0",
21
10
  name,
22
11
  persona,
23
12
  memories: [...memories],
24
13
  state: { ...state }
25
14
  });
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);
15
+
16
+ // src/bridgeSlice.ts
17
+ import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
18
+
19
+ // src/apiSlice.ts
20
+ import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query";
21
+ var sdkApi = createApi({
22
+ reducerPath: "forbocApi",
23
+ baseQuery: fetchBaseQuery({
24
+ baseUrl: "/",
25
+ prepareHeaders: (headers, { getState }) => {
26
+ return headers;
27
+ }
28
+ }),
29
+ tagTypes: ["NPC", "Memory", "Cortex", "Ghost", "Soul", "Bridge"],
30
+ endpoints: (builder) => ({
31
+ // NPC Endpoints
32
+ postDirective: builder.mutation({
33
+ query: ({ npcId, request, apiUrl, apiKey }) => ({
34
+ url: `${apiUrl}/npcs/${npcId}/directive`,
35
+ method: "POST",
36
+ headers: apiKey ? { "Authorization": `Bearer ${apiKey}` } : {},
37
+ body: request
38
+ }),
39
+ invalidatesTags: ["NPC"],
40
+ transformResponse: (response) => response
41
+ }),
42
+ postContext: builder.mutation({
43
+ query: ({ npcId, request, apiUrl, apiKey }) => ({
44
+ url: `${apiUrl}/npcs/${npcId}/context`,
45
+ method: "POST",
46
+ headers: apiKey ? { "Authorization": `Bearer ${apiKey}` } : {},
47
+ body: request
48
+ }),
49
+ invalidatesTags: (result, error, { npcId }) => [{ type: "NPC", id: npcId }],
50
+ transformResponse: (response) => response
51
+ }),
52
+ postVerdict: builder.mutation({
53
+ query: ({ npcId, request, apiUrl, apiKey }) => ({
54
+ url: `${apiUrl}/npcs/${npcId}/verdict`,
55
+ method: "POST",
56
+ headers: apiKey ? { "Authorization": `Bearer ${apiKey}` } : {},
57
+ body: request
58
+ }),
59
+ invalidatesTags: (result, error, { npcId }) => [{ type: "NPC", id: npcId }],
60
+ transformResponse: (response) => {
61
+ const action = response.action ? {
62
+ type: response.action.gaType || response.action.type,
63
+ reason: response.action.actionReason || response.action.reason,
64
+ target: response.action.actionTarget || response.action.target,
65
+ signature: response.action.signature
66
+ } : void 0;
67
+ return {
68
+ ...response,
69
+ action
70
+ };
71
+ }
72
+ }),
73
+ postSpeak: builder.mutation({
74
+ query: ({ npcId, request, apiUrl, apiKey }) => ({
75
+ url: `${apiUrl}/npcs/${npcId}/speak`,
76
+ method: "POST",
77
+ headers: apiKey ? { "Authorization": `Bearer ${apiKey}` } : {},
78
+ body: request
79
+ }),
80
+ invalidatesTags: (result, error, { npcId }) => [{ type: "NPC", id: npcId }],
81
+ transformResponse: (response) => response
82
+ }),
83
+ postDialogue: builder.mutation({
84
+ query: ({ npcId, request, apiUrl, apiKey }) => ({
85
+ url: `${apiUrl}/npcs/${npcId}/dialogue`,
86
+ method: "POST",
87
+ headers: apiKey ? { "Authorization": `Bearer ${apiKey}` } : {},
88
+ body: request
89
+ }),
90
+ invalidatesTags: (result, error, { npcId }) => [{ type: "NPC", id: npcId }],
91
+ transformResponse: (response) => response
92
+ }),
93
+ // Ghost Endpoints
94
+ postGhostRun: builder.mutation({
95
+ query: ({ request, apiUrl }) => ({ url: `${apiUrl}/ghost/run`, method: "POST", body: request }),
96
+ invalidatesTags: ["Ghost"],
97
+ transformResponse: (response) => response
98
+ }),
99
+ getGhostStatus: builder.query({
100
+ query: ({ sessionId, apiUrl }) => ({ url: `${apiUrl}/ghost/${sessionId}/status`, method: "GET" }),
101
+ providesTags: (result, error, { sessionId }) => [{ type: "Ghost", id: sessionId }],
102
+ transformResponse: (response) => response
103
+ }),
104
+ getGhostResults: builder.query({
105
+ query: ({ sessionId, apiUrl }) => ({ url: `${apiUrl}/ghost/${sessionId}/results`, method: "GET" }),
106
+ providesTags: (result, error, { sessionId }) => [{ type: "Ghost", id: sessionId }],
107
+ transformResponse: (response) => response
108
+ }),
109
+ postGhostStop: builder.mutation({
110
+ query: ({ sessionId, apiUrl }) => ({ url: `${apiUrl}/ghost/${sessionId}/stop`, method: "POST" }),
111
+ invalidatesTags: (result, error, { sessionId }) => [{ type: "Ghost", id: sessionId }],
112
+ transformResponse: (response) => ({ stopped: true })
113
+ }),
114
+ getGhostHistory: builder.query({
115
+ query: ({ limit, apiUrl }) => ({ url: `${apiUrl}/ghost/history?limit=${limit}`, method: "GET" }),
116
+ providesTags: ["Ghost"],
117
+ transformResponse: (response) => response
118
+ }),
119
+ // Soul Endpoints
120
+ postSoulExport: builder.mutation({
121
+ query: ({ npcId, request, apiUrl }) => ({ url: `${apiUrl}/npcs/${npcId}/soul/export`, method: "POST", body: request }),
122
+ invalidatesTags: ["Soul"],
123
+ transformResponse: (response) => response
124
+ }),
125
+ getSoulImport: builder.query({
126
+ query: ({ txId, apiUrl }) => ({ url: `${apiUrl}/souls/${txId}`, method: "GET" }),
127
+ providesTags: (result, error, { txId }) => [{ type: "Soul", id: txId }],
128
+ transformResponse: (response) => response
129
+ }),
130
+ getSouls: builder.query({
131
+ query: ({ limit, apiUrl }) => ({ url: `${apiUrl}/souls?limit=${limit}`, method: "GET" }),
132
+ providesTags: ["Soul"],
133
+ transformResponse: (response) => response
134
+ }),
135
+ // Bridge Endpoints
136
+ postBridgeValidate: builder.mutation({
137
+ query: ({ request, npcId, apiUrl }) => ({
138
+ url: npcId ? `${apiUrl}/bridge/validate/${npcId}` : `${apiUrl}/bridge/validate`,
139
+ method: "POST",
140
+ body: request
141
+ }),
142
+ invalidatesTags: ["Bridge"],
143
+ transformResponse: (response) => response.brResult || response
144
+ }),
145
+ getBridgeRules: builder.query({
146
+ query: ({ apiUrl }) => ({ url: `${apiUrl}/rules`, method: "GET" }),
147
+ providesTags: ["Bridge"],
148
+ transformResponse: (response) => response
149
+ }),
150
+ postBridgePreset: builder.mutation({
151
+ query: ({ presetName, apiUrl }) => ({ url: `${apiUrl}/rules/presets/${presetName}`, method: "POST" }),
152
+ invalidatesTags: ["Bridge"],
153
+ transformResponse: (response) => response
154
+ }),
155
+ // Cortex Remote Endpoint
156
+ postCortexComplete: builder.mutation({
157
+ query: ({ cortexId, prompt, options, apiUrl, apiKey }) => ({
158
+ url: `${apiUrl}/cortex/${cortexId}/complete`,
159
+ method: "POST",
160
+ headers: apiKey ? { "Authorization": `Bearer ${apiKey}` } : {},
161
+ body: { prompt, ...options }
162
+ }),
163
+ invalidatesTags: ["Cortex"],
164
+ transformResponse: (response) => response
165
+ }),
166
+ // System Endpoints
167
+ getApiStatus: builder.query({
168
+ query: ({ apiUrl }) => ({ url: `${apiUrl}/status`, method: "GET" }),
169
+ transformResponse: (response) => response
170
+ })
171
+ })
172
+ });
173
+
174
+ // src/bridgeSlice.ts
175
+ var initialState = {
176
+ activePresets: [],
177
+ lastValidation: null,
178
+ status: "idle",
179
+ error: null
31
180
  };
32
- var createAgent = (config) => {
33
- let _state = createInitialState(config.initialState);
34
- const cortex = config.cortex;
35
- const apiUrl = config.apiUrl || "https://api.forboc.ai";
36
- const agentId = config.id || "agent-" + Math.random().toString(36).substring(7);
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;
41
- };
42
- const process = async (input, context = {}) => {
43
- const currentState = getState();
44
- const dirRes = await fetch(`${apiUrl}/agents/${agentId}/directive`, {
45
- method: "POST",
46
- headers: authHeaders,
47
- body: JSON.stringify({ observation: input, agentState: currentState, context })
48
- });
49
- if (!dirRes.ok) throw new Error(`API Directive Error: ${dirRes.status}`);
50
- const directiveData = await dirRes.json();
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(() => []) : [];
56
- const ctxRes = await fetch(`${apiUrl}/agents/${agentId}/context`, {
57
- method: "POST",
58
- headers: authHeaders,
59
- body: JSON.stringify({ memories: recalledMemories, observation: input, agentState: currentState })
60
- });
61
- if (!ctxRes.ok) throw new Error(`API Context Error: ${ctxRes.status}`);
62
- const contextData = await ctxRes.json();
63
- const generatedText = await cortex.complete(contextData.prompt, {
64
- maxTokens: contextData.constraints.maxTokens,
65
- temperature: contextData.constraints.temperature,
66
- stop: contextData.constraints.stop
67
- });
68
- const verRes = await fetch(`${apiUrl}/agents/${agentId}/verdict`, {
69
- method: "POST",
70
- headers: authHeaders,
71
- body: JSON.stringify({ generatedOutput: generatedText, observation: input, agentState: currentState })
72
- });
73
- if (!verRes.ok) throw new Error(`API Verdict Error: ${verRes.status}`);
74
- const verdictData = await verRes.json();
75
- if (!verdictData.valid) {
76
- return { dialogue: "... [Blocked]", action: { type: "BLOCKED", reason: "Validation Failed" } };
181
+ var validateBridgeThunk = createAsyncThunk(
182
+ "bridge/validate",
183
+ async ({ action, context, npcId, apiUrl }, { dispatch: dispatch2, rejectWithValue }) => {
184
+ try {
185
+ const url = apiUrl || "https://api.forboc.ai";
186
+ const data = await dispatch2(sdkApi.endpoints.postBridgeValidate.initiate({
187
+ request: { action, context },
188
+ npcId,
189
+ apiUrl: url
190
+ })).unwrap();
191
+ return data;
192
+ } catch (e) {
193
+ return rejectWithValue(e.message || "Bridge validation failed");
77
194
  }
78
- if (config.memory && verdictData.memoryStore) {
79
- storeMemoriesRecursive(config.memory, verdictData.memoryStore);
195
+ }
196
+ );
197
+ var loadBridgePresetThunk = createAsyncThunk(
198
+ "bridge/loadPreset",
199
+ async ({ presetName, apiUrl }, { dispatch: dispatch2, rejectWithValue }) => {
200
+ try {
201
+ const url = apiUrl || "https://api.forboc.ai";
202
+ return await dispatch2(sdkApi.endpoints.postBridgePreset.initiate({ presetName, apiUrl: url })).unwrap();
203
+ } catch (e) {
204
+ return rejectWithValue(e.message || "Failed to load preset");
80
205
  }
81
- if (verdictData.stateDelta) {
82
- setState(updateAgentState(_state, verdictData.stateDelta));
206
+ }
207
+ );
208
+ var getBridgeRulesThunk = createAsyncThunk(
209
+ "bridge/rules",
210
+ async ({ apiUrl }, { dispatch: dispatch2, rejectWithValue }) => {
211
+ try {
212
+ const url = apiUrl || "https://api.forboc.ai";
213
+ return await dispatch2(sdkApi.endpoints.getBridgeRules.initiate({ apiUrl: url })).unwrap();
214
+ } catch (e) {
215
+ return rejectWithValue(e.message || "Failed to list rulesets");
83
216
  }
84
- return {
85
- dialogue: generatedText,
86
- action: verdictData.action ? { ...verdictData.action, signature: verdictData.signature } : void 0,
87
- thought: generatedText
88
- };
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 })
217
+ }
218
+ );
219
+ var bridgeSlice = createSlice({
220
+ name: "bridge",
221
+ initialState,
222
+ reducers: {
223
+ clearBridgeValidation: (state) => {
224
+ state.lastValidation = null;
225
+ state.status = "idle";
226
+ state.error = null;
227
+ }
228
+ },
229
+ extraReducers: (builder) => {
230
+ builder.addCase(validateBridgeThunk.pending, (state) => {
231
+ state.status = "validating";
232
+ state.error = null;
233
+ }).addCase(validateBridgeThunk.fulfilled, (state, action) => {
234
+ state.status = "idle";
235
+ state.lastValidation = action.payload;
236
+ }).addCase(validateBridgeThunk.rejected, (state, action) => {
237
+ state.status = "error";
238
+ state.error = action.payload;
239
+ state.lastValidation = { valid: false, reason: action.payload };
240
+ }).addCase(loadBridgePresetThunk.pending, (state) => {
241
+ state.status = "loading_preset";
242
+ state.error = null;
243
+ }).addCase(loadBridgePresetThunk.fulfilled, (state, action) => {
244
+ state.status = "idle";
245
+ if (!state.activePresets.find((p) => p.id === action.payload.id)) {
246
+ state.activePresets.push(action.payload);
247
+ }
248
+ }).addCase(loadBridgePresetThunk.rejected, (state, action) => {
249
+ state.status = "error";
250
+ state.error = action.payload;
251
+ }).addCase(getBridgeRulesThunk.fulfilled, (state, action) => {
106
252
  });
107
- if (!res.ok) throw new Error(`API Dialogue Error: ${res.status}`);
108
- const data = await res.json();
109
- return data.diagReply;
110
- };
111
- const exportSoul2 = async () => {
112
- const memories = config.memory ? await config.memory.export().catch(() => []) : [];
113
- return exportToSoul(agentId, "Agent", config.persona, _state, memories);
114
- };
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 });
119
- };
120
- return { process, speak, dialogue, reply: speak, getState, setState, export: exportSoul2, exportSoul: exportSoulToArweave };
121
- };
122
- var fromSoul = async (soul, cortex, memory) => {
123
- const agent = createAgent({
124
- id: soul.id,
125
- cortex,
126
- memory: memory || null,
127
- persona: soul.persona,
128
- initialState: soul.state
129
- });
130
- if (memory && soul.memories && soul.memories.length > 0) {
131
- await memory.import(soul.memories).catch((e) => console.warn("Memory hydration failed:", e));
132
253
  }
133
- return agent;
254
+ });
255
+ var { clearBridgeValidation } = bridgeSlice.actions;
256
+ var bridgeSlice_default = bridgeSlice.reducer;
257
+
258
+ // src/soulSlice.ts
259
+ import { createSlice as createSlice2, createAsyncThunk as createAsyncThunk2 } from "@reduxjs/toolkit";
260
+ var initialState2 = {
261
+ exportStatus: "idle",
262
+ importStatus: "idle",
263
+ lastExport: null,
264
+ lastImport: null,
265
+ availableSouls: [],
266
+ error: null
134
267
  };
268
+ var remoteExportSoulThunk = createAsyncThunk2(
269
+ "soul/export",
270
+ async ({ npcId: argNpcId, apiUrl, memories = [] }, { getState, dispatch: dispatch2, rejectWithValue }) => {
271
+ try {
272
+ const state = getState().npc;
273
+ const npcId = argNpcId || state.activeNpcId;
274
+ const npc = state.entities[npcId];
275
+ if (!npc) throw new Error(`NPC ${npcId} not found`);
276
+ const url = apiUrl || "https://api.forboc.ai";
277
+ const result = await dispatch2(sdkApi.endpoints.postSoulExport.initiate({
278
+ npcId,
279
+ request: { npcIdRef: npcId, persona: npc.persona || "NPC", npcState: npc.state },
280
+ apiUrl: url
281
+ })).unwrap();
282
+ return {
283
+ txId: result.txId,
284
+ url: result.arweaveUrl,
285
+ soul: result.soul
286
+ };
287
+ } catch (e) {
288
+ return rejectWithValue(e.message || "Soul export failed");
289
+ }
290
+ }
291
+ );
292
+ var importSoulFromArweaveThunk = createAsyncThunk2(
293
+ "soul/import",
294
+ async ({ txId, apiUrl }, { dispatch: dispatch2, rejectWithValue }) => {
295
+ try {
296
+ const url = apiUrl || "https://api.forboc.ai";
297
+ const data = await dispatch2(sdkApi.endpoints.getSoulImport.initiate({ txId, apiUrl: url })).unwrap();
298
+ return data;
299
+ } catch (e) {
300
+ return rejectWithValue(e.message || "Soul import failed");
301
+ }
302
+ }
303
+ );
304
+ var getSoulListThunk = createAsyncThunk2(
305
+ "soul/list",
306
+ async ({ limit = 50, apiUrl }, { dispatch: dispatch2, rejectWithValue }) => {
307
+ try {
308
+ const url = apiUrl || "https://api.forboc.ai";
309
+ const data = await dispatch2(sdkApi.endpoints.getSouls.initiate({ limit, apiUrl: url })).unwrap();
310
+ return data.souls || [];
311
+ } catch (e) {
312
+ return rejectWithValue(e.message || "Failed to list souls");
313
+ }
314
+ }
315
+ );
316
+ var soulSlice = createSlice2({
317
+ name: "soul",
318
+ initialState: initialState2,
319
+ reducers: {
320
+ clearSoulState: (state) => {
321
+ state.exportStatus = "idle";
322
+ state.importStatus = "idle";
323
+ state.lastExport = null;
324
+ state.lastImport = null;
325
+ state.error = null;
326
+ }
327
+ },
328
+ extraReducers: (builder) => {
329
+ builder.addCase(remoteExportSoulThunk.pending, (state) => {
330
+ state.exportStatus = "exporting";
331
+ state.error = null;
332
+ }).addCase(remoteExportSoulThunk.fulfilled, (state, action) => {
333
+ state.exportStatus = "success";
334
+ state.lastExport = action.payload;
335
+ }).addCase(remoteExportSoulThunk.rejected, (state, action) => {
336
+ state.exportStatus = "failed";
337
+ state.error = action.payload;
338
+ }).addCase(importSoulFromArweaveThunk.pending, (state) => {
339
+ state.importStatus = "importing";
340
+ state.error = null;
341
+ }).addCase(importSoulFromArweaveThunk.fulfilled, (state, action) => {
342
+ state.importStatus = "success";
343
+ state.lastImport = action.payload;
344
+ }).addCase(importSoulFromArweaveThunk.rejected, (state, action) => {
345
+ state.importStatus = "failed";
346
+ state.error = action.payload;
347
+ }).addCase(getSoulListThunk.fulfilled, (state, action) => {
348
+ state.availableSouls = action.payload;
349
+ });
350
+ }
351
+ });
352
+ var { clearSoulState } = soulSlice.actions;
353
+ var soulSlice_default = soulSlice.reducer;
135
354
 
136
- // src/bridge.ts
137
- var createBridge = (config = {}) => {
138
- const apiUrl = config.apiUrl || "https://api.forboc.ai";
139
- const agentId = config.agentId;
140
- const validate = async (action, context = {}) => {
141
- const endpoint = agentId ? `${apiUrl}/bridge/validate/${agentId}` : `${apiUrl}/bridge/validate`;
355
+ // src/ghostSlice.ts
356
+ import { createSlice as createSlice3, createAsyncThunk as createAsyncThunk3 } from "@reduxjs/toolkit";
357
+ var initialState3 = {
358
+ activeSessionId: null,
359
+ status: null,
360
+ progress: 0,
361
+ results: null,
362
+ history: [],
363
+ loading: false,
364
+ error: null
365
+ };
366
+ var startGhostThunk = createAsyncThunk3(
367
+ "ghost/start",
368
+ async (config, { dispatch: dispatch2, rejectWithValue }) => {
142
369
  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}`);
151
- }
152
- const data = await response.json();
153
- return data.brResult || data;
370
+ const apiUrl = config.apiUrl || "https://api.forboc.ai";
371
+ const data = await dispatch2(sdkApi.endpoints.postGhostRun.initiate({
372
+ request: { testSuite: config.testSuite, duration: config.duration ?? 300 },
373
+ apiUrl
374
+ })).unwrap();
375
+ return {
376
+ sessionId: data.sessionId,
377
+ status: data.runStatus
378
+ };
154
379
  } catch (e) {
155
- console.error("[Bridge] Validation error:", e);
380
+ return rejectWithValue(e.message || "Failed to start Ghost");
381
+ }
382
+ }
383
+ );
384
+ var getGhostStatusThunk = createAsyncThunk3(
385
+ "ghost/status",
386
+ async ({ sessionId, apiUrl }, { dispatch: dispatch2, getState, rejectWithValue }) => {
387
+ try {
388
+ const state = getState().ghost;
389
+ const targetSession = sessionId || state.activeSessionId;
390
+ if (!targetSession) throw new Error("No active Ghost session");
391
+ const url = apiUrl || "https://api.forboc.ai";
392
+ const data = await dispatch2(sdkApi.endpoints.getGhostStatus.initiate({ sessionId: targetSession, apiUrl: url })).unwrap();
156
393
  return {
157
- valid: false,
158
- reason: `Network error reaching validation API: ${e instanceof Error ? e.message : String(e)}`
394
+ sessionId: data.ghostSessionId,
395
+ status: data.ghostStatus,
396
+ progress: data.ghostProgress,
397
+ startedAt: data.ghostStartedAt,
398
+ duration: data.ghostDuration || 0,
399
+ errors: data.ghostErrors
159
400
  };
401
+ } catch (e) {
402
+ return rejectWithValue(e.message || "Failed to get ghost status");
160
403
  }
161
- };
162
- const listRulesets = async () => {
163
- const response = await fetch(`${apiUrl}/rules`);
164
- if (!response.ok) return [];
165
- return response.json();
166
- };
167
- return { validate, listRulesets };
168
- };
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}`);
179
404
  }
180
- return response.json();
181
- };
405
+ );
406
+ var getGhostResultsThunk = createAsyncThunk3(
407
+ "ghost/results",
408
+ async ({ sessionId, apiUrl }, { dispatch: dispatch2, getState, rejectWithValue }) => {
409
+ try {
410
+ const state = getState().ghost;
411
+ const targetSession = sessionId || state.activeSessionId;
412
+ if (!targetSession) throw new Error("No active Ghost session");
413
+ const url = apiUrl || "https://api.forboc.ai";
414
+ const data = await dispatch2(sdkApi.endpoints.getGhostResults.initiate({ sessionId: targetSession, apiUrl: url })).unwrap();
415
+ return {
416
+ sessionId: data.resultsSessionId,
417
+ totalTests: data.resultsTotalTests,
418
+ passed: data.resultsPassed,
419
+ failed: data.resultsFailed,
420
+ skipped: data.resultsSkipped,
421
+ duration: data.resultsDuration,
422
+ tests: (data.resultsTests || []).map((t) => ({
423
+ name: t.testName,
424
+ passed: t.testPassed,
425
+ duration: t.testDuration,
426
+ error: t.testError,
427
+ screenshot: t.testScreenshot
428
+ })),
429
+ coverage: data.resultsCoverage,
430
+ metrics: Object.fromEntries(data.resultsMetrics || [])
431
+ };
432
+ } catch (e) {
433
+ return rejectWithValue(e.message || "Failed to get ghost results");
434
+ }
435
+ }
436
+ );
437
+ var stopGhostThunk = createAsyncThunk3(
438
+ "ghost/stop",
439
+ async ({ sessionId, apiUrl }, { dispatch: dispatch2, getState, rejectWithValue }) => {
440
+ try {
441
+ const state = getState().ghost;
442
+ const targetSession = sessionId || state.activeSessionId;
443
+ if (!targetSession) throw new Error("No active Ghost session");
444
+ const url = apiUrl || "https://api.forboc.ai";
445
+ await dispatch2(sdkApi.endpoints.postGhostStop.initiate({ sessionId: targetSession, apiUrl: url })).unwrap();
446
+ return { stopped: true };
447
+ } catch (e) {
448
+ return { stopped: true };
449
+ }
450
+ }
451
+ );
452
+ var getGhostHistoryThunk = createAsyncThunk3(
453
+ "ghost/history",
454
+ async ({ limit = 10, apiUrl }, { dispatch: dispatch2, rejectWithValue }) => {
455
+ try {
456
+ const url = apiUrl || "https://api.forboc.ai";
457
+ const data = await dispatch2(sdkApi.endpoints.getGhostHistory.initiate({ limit, apiUrl: url })).unwrap();
458
+ return (data.sessions || []).map((s) => ({
459
+ sessionId: s.sessionId,
460
+ testSuite: s.testSuite,
461
+ startedAt: s.startedAt,
462
+ completedAt: s.completedAt,
463
+ status: s.status,
464
+ passRate: s.passRate
465
+ }));
466
+ } catch (e) {
467
+ return rejectWithValue(e.message || "Failed to get ghost history");
468
+ }
469
+ }
470
+ );
471
+ var ghostSlice = createSlice3({
472
+ name: "ghost",
473
+ initialState: initialState3,
474
+ reducers: {
475
+ clearGhostSession: (state) => {
476
+ state.activeSessionId = null;
477
+ state.status = null;
478
+ state.progress = 0;
479
+ state.results = null;
480
+ }
481
+ },
482
+ extraReducers: (builder) => {
483
+ builder.addCase(startGhostThunk.pending, (state) => {
484
+ state.loading = true;
485
+ state.error = null;
486
+ }).addCase(startGhostThunk.fulfilled, (state, action) => {
487
+ state.loading = false;
488
+ state.activeSessionId = action.payload.sessionId;
489
+ state.status = "running";
490
+ state.progress = 0;
491
+ }).addCase(startGhostThunk.rejected, (state, action) => {
492
+ state.loading = false;
493
+ state.error = action.payload;
494
+ }).addCase(getGhostStatusThunk.fulfilled, (state, action) => {
495
+ state.status = action.payload.status;
496
+ state.progress = action.payload.progress;
497
+ }).addCase(getGhostResultsThunk.fulfilled, (state, action) => {
498
+ state.results = action.payload;
499
+ state.status = "completed";
500
+ }).addCase(stopGhostThunk.fulfilled, (state) => {
501
+ state.status = "failed";
502
+ }).addCase(getGhostHistoryThunk.fulfilled, (state, action) => {
503
+ state.history = action.payload;
504
+ });
505
+ }
506
+ });
507
+ var { clearGhostSession } = ghostSlice.actions;
508
+ var ghostSlice_default = ghostSlice.reducer;
182
509
 
183
510
  // src/utils.ts
511
+ var SDK_VERSION = "0.5.9";
512
+ var generateNPCId = () => `ag_${Date.now().toString(36)}`;
184
513
  var delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
185
514
  var pipe = (...fns) => (initialValue) => fns.reduce((acc, fn) => fn(acc), initialValue);
186
515
  var memoiseAsync = (fn) => {
@@ -207,161 +536,6 @@ var memoise = (fn) => {
207
536
  };
208
537
  };
209
538
 
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();
224
- return {
225
- sessionId: data.sessionId,
226
- status: data.runStatus
227
- };
228
- };
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();
237
- return {
238
- sessionId: data.ghostSessionId,
239
- status: data.ghostStatus,
240
- progress: data.ghostProgress,
241
- startedAt: data.ghostStartedAt,
242
- duration: data.ghostDuration,
243
- errors: data.ghostErrors
244
- };
245
- };
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();
254
- return {
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 || [])
270
- };
271
- };
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`);
275
- }
276
- try {
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`);
281
- } catch (e) {
282
- }
283
- await delay(pollIntervalMs);
284
- return pollForCompletion(sessionId, pollIntervalMs, timeoutMs, startTime, apiUrl, onProgress);
285
- };
286
- var waitForGhostCompletion = (sessionId, pollIntervalMs = 5e3, timeoutMs = 3e5, apiUrl, onProgress) => pollForCompletion(sessionId, pollIntervalMs, timeoutMs, Date.now(), apiUrl, onProgress);
287
- var stopGhostSession = async (sessionId, apiUrl) => {
288
- const url = apiUrl || DEFAULT_API_URL;
289
- try {
290
- const response = await fetch(`${url}/ghost/${sessionId}/stop`, { method: "POST" });
291
- return { stopped: response.ok };
292
- } catch (e) {
293
- return { stopped: true };
294
- }
295
- };
296
- var getGhostHistory = async (limit = 10, apiUrl) => {
297
- const url = apiUrl || DEFAULT_API_URL;
298
- try {
299
- const response = await fetch(`${url}/ghost/history?limit=${limit}`);
300
- if (!response.ok) return [];
301
- const data = await response.json();
302
- return data.sessions.map((s) => ({
303
- sessionId: s.sessionId,
304
- testSuite: s.testSuite,
305
- startedAt: s.startedAt,
306
- completedAt: s.completedAt,
307
- status: s.status,
308
- passRate: s.passRate
309
- }));
310
- } catch (e) {
311
- return [];
312
- }
313
- };
314
- var createGhost = (config) => {
315
- let _sessionId = null;
316
- const apiUrl = config.apiUrl || DEFAULT_API_URL;
317
- const run = async () => {
318
- const result = await startGhostSession(config);
319
- _sessionId = result.sessionId;
320
- return _sessionId;
321
- };
322
- const ensureSessionId = () => {
323
- if (!_sessionId) throw new Error("Ghost session not started");
324
- return _sessionId;
325
- };
326
- const status = async () => getGhostStatus(ensureSessionId(), apiUrl);
327
- const results = async () => getGhostResults(ensureSessionId(), apiUrl);
328
- const stop = async () => {
329
- if (_sessionId) await stopGhostSession(_sessionId, apiUrl);
330
- _sessionId = null;
331
- };
332
- const waitForCompletion = (pollIntervalMs, timeoutMs, onProgress) => waitForGhostCompletion(ensureSessionId(), pollIntervalMs, timeoutMs, apiUrl, onProgress);
333
- return { run, status, results, stop, waitForCompletion };
334
- };
335
-
336
- // src/cortex-remote.ts
337
- var createRemoteCortex = (apiUrl, cortexId = "local", apiKey) => {
338
- const authHeaders = {
339
- "Content-Type": "application/json",
340
- ...apiKey ? { "Authorization": `Bearer ${apiKey}` } : {}
341
- };
342
- const init = async () => ({
343
- id: `remote_${Date.now()}`,
344
- model: "api-integrated",
345
- ready: true,
346
- engine: "remote"
347
- });
348
- const complete = async (prompt, options) => {
349
- const response = await fetch(`${apiUrl}/cortex/${cortexId}/complete`, {
350
- method: "POST",
351
- headers: authHeaders,
352
- body: JSON.stringify({ prompt, ...options })
353
- });
354
- if (!response.ok) throw new Error(`Remote Cortex failed: ${response.statusText}`);
355
- const data = await response.json();
356
- return data.text;
357
- };
358
- const completeStream = async function* (prompt, options) {
359
- const result = await complete(prompt, options);
360
- yield result;
361
- };
362
- return { init, complete, completeStream };
363
- };
364
-
365
539
  // src/stream.ts
366
540
  var consumeStream = async (stream, onChunk, acc = "", delayMs = 0) => {
367
541
  const { value, done } = await stream.next();
@@ -386,40 +560,453 @@ var streamFromCortexWithDelay = async (cortex, prompt, onChunk, options) => {
386
560
  return consumeStream(cortex.completeStream(prompt, completionOptions), onChunk, "", delayMs);
387
561
  };
388
562
 
389
- // src/index.ts
390
- var SDK_VERSION = "0.5.9";
563
+ // src/store.ts
564
+ import { configureStore } from "@reduxjs/toolkit";
565
+
566
+ // src/npcSlice.ts
567
+ import { createSlice as createSlice4, createEntityAdapter } from "@reduxjs/toolkit";
568
+ var npcAdapter = createEntityAdapter();
569
+ var npcSlice = createSlice4({
570
+ name: "npc",
571
+ initialState: npcAdapter.getInitialState({
572
+ activeNpcId: ""
573
+ }),
574
+ reducers: {
575
+ setNPCInfo: (state, action) => {
576
+ const { id, persona, initialState: initialState5 } = action.payload;
577
+ npcAdapter.upsertOne(state, {
578
+ id,
579
+ persona,
580
+ state: initialState5 || {},
581
+ history: [],
582
+ isBlocked: false
583
+ });
584
+ state.activeNpcId = id;
585
+ },
586
+ setActiveNPC: (state, action) => {
587
+ state.activeNpcId = action.payload;
588
+ },
589
+ setNPCState: (state, action) => {
590
+ const { id, state: npcState } = action.payload;
591
+ npcAdapter.updateOne(state, { id, changes: { state: npcState } });
592
+ },
593
+ updateNPCState: (state, action) => {
594
+ const { id, delta } = action.payload;
595
+ const npc = state.entities[id];
596
+ if (npc) {
597
+ npc.state = { ...npc.state, ...delta };
598
+ }
599
+ },
600
+ addToHistory: (state, action) => {
601
+ const { id, role, content } = action.payload;
602
+ const npc = state.entities[id];
603
+ if (npc) {
604
+ npc.history.push({ role, content });
605
+ }
606
+ },
607
+ setHistory: (state, action) => {
608
+ const { id, history } = action.payload;
609
+ npcAdapter.updateOne(state, { id, changes: { history } });
610
+ },
611
+ setLastAction: (state, action) => {
612
+ const { id, action: npcAction } = action.payload;
613
+ npcAdapter.updateOne(state, { id, changes: { lastAction: npcAction } });
614
+ },
615
+ blockAction: (state, action) => {
616
+ const { id, reason } = action.payload;
617
+ npcAdapter.updateOne(state, { id, changes: { isBlocked: true, blockReason: reason } });
618
+ },
619
+ clearBlock: (state, action) => {
620
+ npcAdapter.updateOne(state, { id: action.payload, changes: { isBlocked: false, blockReason: void 0 } });
621
+ },
622
+ removeNPC: npcAdapter.removeOne
623
+ }
624
+ });
625
+ var {
626
+ setNPCInfo,
627
+ setActiveNPC,
628
+ setNPCState,
629
+ updateNPCState,
630
+ addToHistory,
631
+ setHistory,
632
+ setLastAction,
633
+ blockAction,
634
+ clearBlock,
635
+ removeNPC
636
+ } = npcSlice.actions;
637
+ var {
638
+ selectById: selectNPCById,
639
+ selectIds: selectNPCIds,
640
+ selectEntities: selectNPCEntities,
641
+ selectAll: selectAllNPCs,
642
+ selectTotal: selectTotalNPCs
643
+ } = npcAdapter.getSelectors((state) => state.npc);
644
+ var selectActiveNpcId = (state) => state.npc.activeNpcId;
645
+ var selectActiveNPC = (state) => {
646
+ const id = selectActiveNpcId(state);
647
+ return state.npc.entities[id];
648
+ };
649
+ var npcSlice_default = npcSlice.reducer;
650
+
651
+ // src/cortexSlice.ts
652
+ import { createSlice as createSlice5, createAsyncThunk as createAsyncThunk4 } from "@reduxjs/toolkit";
653
+ var initialState4 = {
654
+ status: {
655
+ id: "init",
656
+ model: "none",
657
+ ready: false,
658
+ engine: "mock"
659
+ },
660
+ isInitializing: false
661
+ };
662
+ var initRemoteCortexThunk = createAsyncThunk4(
663
+ "cortex/initRemote",
664
+ async ({ model = "api-integrated" }) => {
665
+ return {
666
+ id: `remote_${Date.now()}`,
667
+ model,
668
+ ready: true,
669
+ engine: "remote"
670
+ };
671
+ }
672
+ );
673
+ var completeRemoteThunk = createAsyncThunk4(
674
+ "cortex/completeRemote",
675
+ async ({ cortexId, prompt, options, apiUrl, apiKey }, { dispatch: dispatch2, rejectWithValue }) => {
676
+ try {
677
+ const data = await dispatch2(sdkApi.endpoints.postCortexComplete.initiate({
678
+ cortexId,
679
+ prompt,
680
+ options,
681
+ apiUrl,
682
+ apiKey
683
+ })).unwrap();
684
+ return data.text;
685
+ } catch (e) {
686
+ return rejectWithValue(e.message || "Remote completing failed");
687
+ }
688
+ }
689
+ );
690
+ var cortexSlice = createSlice5({
691
+ name: "cortex",
692
+ initialState: initialState4,
693
+ reducers: {
694
+ cortexInitStart: (state) => {
695
+ state.isInitializing = true;
696
+ state.error = void 0;
697
+ },
698
+ cortexInitSuccess: (state, action) => {
699
+ state.status = action.payload;
700
+ state.isInitializing = false;
701
+ state.error = void 0;
702
+ },
703
+ cortexInitFailed: (state, action) => {
704
+ state.isInitializing = false;
705
+ state.error = action.payload;
706
+ },
707
+ setCortexStatus: (state, action) => {
708
+ state.status = action.payload;
709
+ }
710
+ },
711
+ extraReducers: (builder) => {
712
+ builder.addCase(initRemoteCortexThunk.fulfilled, (state, action) => {
713
+ state.status = action.payload;
714
+ state.isInitializing = false;
715
+ }).addCase(completeRemoteThunk.rejected, (state, action) => {
716
+ state.error = action.payload;
717
+ });
718
+ }
719
+ });
720
+ var {
721
+ cortexInitStart,
722
+ cortexInitSuccess,
723
+ cortexInitFailed,
724
+ setCortexStatus
725
+ } = cortexSlice.actions;
726
+ var cortexSlice_default = cortexSlice.reducer;
727
+
728
+ // src/memorySlice.ts
729
+ import { createSlice as createSlice6, createEntityAdapter as createEntityAdapter2 } from "@reduxjs/toolkit";
730
+ var memoryAdapter = createEntityAdapter2();
731
+ var memorySlice = createSlice6({
732
+ name: "memory",
733
+ initialState: memoryAdapter.getInitialState({
734
+ storageStatus: "idle",
735
+ recallStatus: "idle",
736
+ error: null,
737
+ lastRecalledIds: []
738
+ }),
739
+ reducers: {
740
+ memoryStoreStart: (state) => {
741
+ state.storageStatus = "storing";
742
+ state.error = null;
743
+ },
744
+ memoryStoreSuccess: (state, action) => {
745
+ state.storageStatus = "idle";
746
+ memoryAdapter.upsertOne(state, action.payload);
747
+ },
748
+ memoryStoreFailed: (state, action) => {
749
+ state.storageStatus = "error";
750
+ state.error = action.payload;
751
+ },
752
+ memoryRecallStart: (state) => {
753
+ state.recallStatus = "recalling";
754
+ state.error = null;
755
+ },
756
+ memoryRecallSuccess: (state, action) => {
757
+ state.recallStatus = "idle";
758
+ memoryAdapter.upsertMany(state, action.payload);
759
+ state.lastRecalledIds = action.payload.map((m) => memoryAdapter.selectId(m));
760
+ },
761
+ memoryRecallFailed: (state, action) => {
762
+ state.recallStatus = "error";
763
+ state.error = action.payload;
764
+ },
765
+ memoryClear: (state) => {
766
+ memoryAdapter.removeAll(state);
767
+ state.lastRecalledIds = [];
768
+ }
769
+ }
770
+ });
771
+ var {
772
+ memoryStoreStart,
773
+ memoryStoreSuccess,
774
+ memoryStoreFailed,
775
+ memoryRecallStart,
776
+ memoryRecallSuccess,
777
+ memoryRecallFailed,
778
+ memoryClear
779
+ } = memorySlice.actions;
780
+ var {
781
+ selectById: selectMemoryById,
782
+ selectAll: selectAllMemories
783
+ } = memoryAdapter.getSelectors((state) => state.memory);
784
+ var selectLastRecalledMemories = (state) => {
785
+ return state.memory.lastRecalledIds.map((id) => state.memory.entities[id]).filter(Boolean);
786
+ };
787
+ var memorySlice_default = memorySlice.reducer;
788
+
789
+ // src/listeners.ts
790
+ import { createListenerMiddleware } from "@reduxjs/toolkit";
791
+ var sdkListenerMiddleware = createListenerMiddleware();
792
+ var startAppListening = sdkListenerMiddleware.startListening.withTypes();
793
+ startAppListening({
794
+ actionCreator: removeNPC,
795
+ effect: async (action, listenerApi) => {
796
+ const state = listenerApi.getState();
797
+ if (action.payload === state.npc.activeNpcId) {
798
+ listenerApi.dispatch(memoryClear());
799
+ }
800
+ }
801
+ });
802
+
803
+ // src/store.ts
804
+ var createSDKStore = (extraReducers = {}) => {
805
+ return configureStore({
806
+ reducer: {
807
+ [sdkApi.reducerPath]: sdkApi.reducer,
808
+ npc: npcSlice_default,
809
+ cortex: cortexSlice_default,
810
+ memory: memorySlice_default,
811
+ ghost: ghostSlice_default,
812
+ soul: soulSlice_default,
813
+ bridge: bridgeSlice_default,
814
+ ...extraReducers
815
+ },
816
+ middleware: (getDefaultMiddleware) => getDefaultMiddleware({
817
+ serializableCheck: false
818
+ // Allow non-serializable for engine refs in specific slices if needed, though we try to keep them external
819
+ }).prepend(sdkListenerMiddleware.middleware).concat(sdkApi.middleware)
820
+ });
821
+ };
822
+ var store = createSDKStore();
823
+ var dispatch = store.dispatch;
824
+
825
+ // src/thunks.ts
826
+ import { createAsyncThunk as createAsyncThunk5 } from "@reduxjs/toolkit";
827
+ var processNPC = createAsyncThunk5(
828
+ "npc/process",
829
+ async ({ npcId: argNpcId, text, context = {}, apiUrl, apiKey, memory, cortex, persona: argPersona }, { getState, dispatch: dispatch2 }) => {
830
+ const stateNpcId = selectActiveNpcId(getState());
831
+ const activeNpc = selectActiveNPC(getState());
832
+ const npcId = argNpcId || stateNpcId;
833
+ const persona = argPersona || activeNpc?.persona;
834
+ const currentState = activeNpc?.state || {};
835
+ if (argNpcId && argNpcId !== stateNpcId) {
836
+ dispatch2(setNPCInfo({ id: argNpcId, persona: persona || "Default" }));
837
+ }
838
+ const directiveResult = await dispatch2(
839
+ sdkApi.endpoints.postDirective.initiate({ npcId, request: { observation: text, npcState: currentState, context }, apiUrl, apiKey })
840
+ ).unwrap();
841
+ const recalledMemories = memory && directiveResult.memoryRecall ? await memory.recall(
842
+ directiveResult.memoryRecall.query,
843
+ directiveResult.memoryRecall.limit,
844
+ directiveResult.memoryRecall.threshold
845
+ ).then((mems) => mems.map((m) => ({ text: m.text, type: m.type, importance: m.importance }))).catch(() => []) : [];
846
+ const contextResult = await dispatch2(
847
+ sdkApi.endpoints.postContext.initiate({ npcId, request: { memories: recalledMemories, observation: text, npcState: currentState, persona: persona || "Default" }, apiUrl, apiKey })
848
+ ).unwrap();
849
+ let generatedText = "";
850
+ if (cortex) {
851
+ generatedText = await cortex.complete(contextResult.prompt, {
852
+ maxTokens: contextResult.constraints.maxTokens,
853
+ temperature: contextResult.constraints.temperature,
854
+ stop: contextResult.constraints.stop
855
+ });
856
+ } else {
857
+ const remoteResult = await dispatch2(sdkApi.endpoints.postCortexComplete.initiate({
858
+ cortexId: "remote",
859
+ // Or generic
860
+ prompt: contextResult.prompt,
861
+ options: contextResult.constraints,
862
+ apiUrl,
863
+ apiKey
864
+ })).unwrap();
865
+ generatedText = remoteResult.text || "";
866
+ }
867
+ const verdictResult = await dispatch2(
868
+ sdkApi.endpoints.postVerdict.initiate({ npcId, request: { generatedOutput: generatedText, observation: text, npcState: currentState }, apiUrl, apiKey })
869
+ ).unwrap();
870
+ if (!verdictResult.valid) {
871
+ dispatch2(blockAction({ id: npcId, reason: "Validation Failed" }));
872
+ return { dialogue: "... [Blocked]", action: { type: "BLOCKED", reason: "Validation Failed" } };
873
+ }
874
+ if (memory && verdictResult.memoryStore) {
875
+ for (const inst of verdictResult.memoryStore) {
876
+ await memory.store(inst.text, inst.type, inst.importance).catch((e) => console.warn("Memory store failed:", e));
877
+ }
878
+ }
879
+ if (verdictResult.stateDelta) {
880
+ dispatch2(updateNPCState({ id: npcId, delta: verdictResult.stateDelta }));
881
+ }
882
+ const action = verdictResult.action ? { ...verdictResult.action, signature: verdictResult.signature } : void 0;
883
+ dispatch2(setLastAction({ id: npcId, action }));
884
+ dispatch2(addToHistory({ id: npcId, role: "user", content: text }));
885
+ dispatch2(addToHistory({ id: npcId, role: "assistant", content: verdictResult.dialogue }));
886
+ return {
887
+ dialogue: verdictResult.dialogue,
888
+ action,
889
+ thought: verdictResult.dialogue
890
+ };
891
+ }
892
+ );
893
+ var speakNPC = createAsyncThunk5(
894
+ "npc/speak",
895
+ async ({ npcId: argNpcId, text, context = {}, apiUrl, apiKey }, { getState, dispatch: dispatch2 }) => {
896
+ const stateNpcId = selectActiveNpcId(getState());
897
+ const activeNpc = selectActiveNPC(getState());
898
+ const npcId = argNpcId || stateNpcId;
899
+ const currentState = activeNpc?.state || {};
900
+ const data = await dispatch2(sdkApi.endpoints.postSpeak.initiate({
901
+ npcId,
902
+ request: { speakMessage: text, speakContext: context, speakNPCState: currentState },
903
+ apiUrl,
904
+ apiKey
905
+ })).unwrap();
906
+ if (data.speakHistory) {
907
+ dispatch2(setHistory({ id: npcId, history: data.speakHistory }));
908
+ }
909
+ return data.speakReply;
910
+ }
911
+ );
912
+ var dialogueNPC = createAsyncThunk5(
913
+ "npc/dialogue",
914
+ async ({ npcId: argNpcId, text, context = [], apiUrl, apiKey }, { getState, dispatch: dispatch2 }) => {
915
+ const stateNpcId = selectActiveNpcId(getState());
916
+ const npcId = argNpcId || stateNpcId;
917
+ const data = await dispatch2(sdkApi.endpoints.postDialogue.initiate({
918
+ npcId,
919
+ request: { diagMessage: text, diagContext: context },
920
+ apiUrl,
921
+ apiKey
922
+ })).unwrap();
923
+ return data.diagReply;
924
+ }
925
+ );
926
+ var localExportSoulThunk = createAsyncThunk5(
927
+ "npc/localExportSoul",
928
+ async ({ id, memory }, { getState }) => {
929
+ const state = getState().npc;
930
+ const npcId = id || state.activeNpcId;
931
+ const npc = state.entities[npcId];
932
+ if (!npc) throw new Error(`NPC ${npcId} not found`);
933
+ const memories = memory ? await memory.export().catch(() => []) : [];
934
+ return exportToSoul(npcId, "NPC", npc.persona, npc.state, memories);
935
+ }
936
+ );
391
937
  export {
392
938
  SDK_VERSION,
393
- createAgent,
394
- createBridge,
395
- createGhost,
939
+ addToHistory,
940
+ blockAction,
941
+ bridgeSlice,
942
+ clearBlock,
943
+ clearBridgeValidation,
944
+ clearGhostSession,
945
+ clearSoulState,
946
+ completeRemoteThunk,
947
+ cortexInitFailed,
948
+ cortexInitStart,
949
+ cortexInitSuccess,
950
+ cortexSlice,
396
951
  createInitialState,
397
- createRemoteCortex,
398
- createSoul,
399
- createSoulInstance,
952
+ createSDKStore,
400
953
  delay,
401
- deserializeSoul,
402
- exportSoul,
954
+ dialogueNPC,
955
+ dispatch,
403
956
  exportToSoul,
404
- fromSoul,
405
- getGhostHistory,
406
- getGhostResults,
407
- getGhostStatus,
408
- getSoulList,
409
- importSoulFromArweave,
410
- loadPreset,
957
+ generateNPCId,
958
+ getBridgeRulesThunk,
959
+ getGhostHistoryThunk,
960
+ getGhostResultsThunk,
961
+ getGhostStatusThunk,
962
+ getSoulListThunk,
963
+ ghostSlice,
964
+ importSoulFromArweaveThunk,
965
+ initRemoteCortexThunk,
966
+ loadBridgePresetThunk,
967
+ localExportSoulThunk,
411
968
  memoise,
412
969
  memoiseAsync,
970
+ memoryClear,
971
+ memoryRecallFailed,
972
+ memoryRecallStart,
973
+ memoryRecallSuccess,
974
+ memorySlice,
975
+ memoryStoreFailed,
976
+ memoryStoreStart,
977
+ memoryStoreSuccess,
978
+ npcSlice,
413
979
  pipe,
414
- serializeSoul,
415
- startGhostSession,
416
- stopGhostSession,
980
+ processNPC,
981
+ remoteExportSoulThunk,
982
+ removeNPC,
983
+ sdkApi,
984
+ selectActiveNPC,
985
+ selectActiveNpcId,
986
+ selectAllMemories,
987
+ selectAllNPCs,
988
+ selectLastRecalledMemories,
989
+ selectMemoryById,
990
+ selectNPCById,
991
+ selectNPCEntities,
992
+ selectNPCIds,
993
+ selectTotalNPCs,
994
+ setActiveNPC,
995
+ setCortexStatus,
996
+ setHistory,
997
+ setLastAction,
998
+ setNPCInfo,
999
+ setNPCState,
1000
+ soulSlice,
1001
+ speakNPC,
1002
+ startGhostThunk,
1003
+ stopGhostThunk,
1004
+ store,
417
1005
  streamFromCortex,
418
1006
  streamFromCortexWithDelay,
419
1007
  streamToCallback,
420
1008
  streamToString,
421
- updateAgentState,
422
- validateAction,
423
- validateSoul,
424
- waitForGhostCompletion
1009
+ updateNPCState,
1010
+ updateNPCStateLocally,
1011
+ validateBridgeThunk
425
1012
  };