@forbocai/core 0.6.2 → 0.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -205,6 +205,16 @@ var sdkApi = createApi({
205
205
  invalidatesTags: ["Soul"],
206
206
  transformResponse: (response) => response
207
207
  }),
208
+ postSoulExportConfirm: builder.mutation({
209
+ query: ({ npcId, request, apiUrl, apiKey }) => ({
210
+ url: `${apiUrl}/npcs/${npcId}/soul/confirm`,
211
+ method: "POST",
212
+ headers: apiKey ? { "Authorization": `Bearer ${apiKey}` } : {},
213
+ body: request
214
+ }),
215
+ invalidatesTags: ["Soul"],
216
+ transformResponse: (response) => response
217
+ }),
208
218
  getSoulImport: builder.query({
209
219
  query: ({ txId, apiUrl, apiKey }) => ({
210
220
  url: `${apiUrl}/souls/${txId}`,
@@ -313,6 +323,16 @@ var sdkApi = createApi({
313
323
  invalidatesTags: ["NPC"],
314
324
  transformResponse: (response) => response
315
325
  }),
326
+ postNpcImportConfirm: builder.mutation({
327
+ query: ({ request, apiUrl, apiKey }) => ({
328
+ url: `${apiUrl}/npcs/import/confirm`,
329
+ method: "POST",
330
+ headers: apiKey ? { "Authorization": `Bearer ${apiKey}` } : {},
331
+ body: request
332
+ }),
333
+ invalidatesTags: ["NPC"],
334
+ transformResponse: (response) => response
335
+ }),
316
336
  // Cortex Remote Endpoint
317
337
  postCortexComplete: builder.mutation({
318
338
  query: ({ cortexId, prompt, options, apiUrl, apiKey }) => ({
@@ -332,6 +352,25 @@ var sdkApi = createApi({
332
352
  })
333
353
  });
334
354
 
355
+ // src/errors.ts
356
+ var extractThunkErrorMessage = (error, fallback) => {
357
+ if (typeof error === "string") return error;
358
+ if (error && typeof error === "object") {
359
+ const e = error;
360
+ if (typeof e.data === "object" && e.data?.message) return String(e.data.message);
361
+ if (typeof e.data === "string") return e.data;
362
+ if (e.message) return e.message;
363
+ if (e.error) return e.error;
364
+ }
365
+ return fallback;
366
+ };
367
+ var requireApiKeyGuidance = (apiUrl, apiKey) => {
368
+ const normalized = apiUrl.toLowerCase();
369
+ if (normalized.includes("api.forboc.ai") && !apiKey) {
370
+ throw new Error("Missing API key. Set FORBOCAI_API_KEY (or run `forboc config set apiKey <key>`) for production API calls.");
371
+ }
372
+ };
373
+
335
374
  // src/bridgeSlice.ts
336
375
  var initialState = {
337
376
  activePresets: [],
@@ -346,6 +385,7 @@ var validateBridgeThunk = createAsyncThunk(
346
385
  async ({ action, context, npcId, apiUrl, apiKey }, { dispatch: dispatch2, rejectWithValue }) => {
347
386
  try {
348
387
  const url = apiUrl || "https://api.forboc.ai";
388
+ requireApiKeyGuidance(url, apiKey);
349
389
  const data = await dispatch2(sdkApi.endpoints.postBridgeValidate.initiate({
350
390
  request: { action, context },
351
391
  npcId,
@@ -353,8 +393,8 @@ var validateBridgeThunk = createAsyncThunk(
353
393
  apiKey
354
394
  })).unwrap();
355
395
  return data;
356
- } catch (e) {
357
- return rejectWithValue(e.message || "Bridge validation failed");
396
+ } catch (error) {
397
+ return rejectWithValue(extractThunkErrorMessage(error, "Bridge validation failed"));
358
398
  }
359
399
  }
360
400
  );
@@ -363,9 +403,10 @@ var loadBridgePresetThunk = createAsyncThunk(
363
403
  async ({ presetName, apiUrl, apiKey }, { dispatch: dispatch2, rejectWithValue }) => {
364
404
  try {
365
405
  const url = apiUrl || "https://api.forboc.ai";
406
+ requireApiKeyGuidance(url, apiKey);
366
407
  return await dispatch2(sdkApi.endpoints.postBridgePreset.initiate({ presetName, apiUrl: url, apiKey })).unwrap();
367
- } catch (e) {
368
- return rejectWithValue(e.message || "Failed to load preset");
408
+ } catch (error) {
409
+ return rejectWithValue(extractThunkErrorMessage(error, "Failed to load preset"));
369
410
  }
370
411
  }
371
412
  );
@@ -374,9 +415,10 @@ var getBridgeRulesThunk = createAsyncThunk(
374
415
  async ({ apiUrl, apiKey }, { dispatch: dispatch2, rejectWithValue }) => {
375
416
  try {
376
417
  const url = apiUrl || "https://api.forboc.ai";
418
+ requireApiKeyGuidance(url, apiKey);
377
419
  return await dispatch2(sdkApi.endpoints.getBridgeRules.initiate({ apiUrl: url, apiKey })).unwrap();
378
- } catch (e) {
379
- return rejectWithValue(e.message || "Failed to list bridge rules");
420
+ } catch (error) {
421
+ return rejectWithValue(extractThunkErrorMessage(error, "Failed to list bridge rules"));
380
422
  }
381
423
  }
382
424
  );
@@ -385,9 +427,10 @@ var listRulesetsThunk = createAsyncThunk(
385
427
  async ({ apiUrl, apiKey }, { dispatch: dispatch2, rejectWithValue }) => {
386
428
  try {
387
429
  const url = apiUrl || "https://api.forboc.ai";
430
+ requireApiKeyGuidance(url, apiKey);
388
431
  return await dispatch2(sdkApi.endpoints.getRulesets.initiate({ apiUrl: url, apiKey })).unwrap();
389
- } catch (e) {
390
- return rejectWithValue(e.message || "Failed to list rulesets");
432
+ } catch (error) {
433
+ return rejectWithValue(extractThunkErrorMessage(error, "Failed to list rulesets"));
391
434
  }
392
435
  }
393
436
  );
@@ -396,9 +439,10 @@ var listRulePresetsThunk = createAsyncThunk(
396
439
  async ({ apiUrl, apiKey }, { dispatch: dispatch2, rejectWithValue }) => {
397
440
  try {
398
441
  const url = apiUrl || "https://api.forboc.ai";
442
+ requireApiKeyGuidance(url, apiKey);
399
443
  return await dispatch2(sdkApi.endpoints.getRulePresets.initiate({ apiUrl: url, apiKey })).unwrap();
400
- } catch (e) {
401
- return rejectWithValue(e.message || "Failed to list rule presets");
444
+ } catch (error) {
445
+ return rejectWithValue(extractThunkErrorMessage(error, "Failed to list rule presets"));
402
446
  }
403
447
  }
404
448
  );
@@ -407,9 +451,10 @@ var registerRulesetThunk = createAsyncThunk(
407
451
  async ({ ruleset, apiUrl, apiKey }, { dispatch: dispatch2, rejectWithValue }) => {
408
452
  try {
409
453
  const url = apiUrl || "https://api.forboc.ai";
454
+ requireApiKeyGuidance(url, apiKey);
410
455
  return await dispatch2(sdkApi.endpoints.postRuleRegister.initiate({ request: ruleset, apiUrl: url, apiKey })).unwrap();
411
- } catch (e) {
412
- return rejectWithValue(e.message || "Failed to register ruleset");
456
+ } catch (error) {
457
+ return rejectWithValue(extractThunkErrorMessage(error, "Failed to register ruleset"));
413
458
  }
414
459
  }
415
460
  );
@@ -418,9 +463,10 @@ var deleteRulesetThunk = createAsyncThunk(
418
463
  async ({ rulesetId, apiUrl, apiKey }, { dispatch: dispatch2, rejectWithValue }) => {
419
464
  try {
420
465
  const url = apiUrl || "https://api.forboc.ai";
466
+ requireApiKeyGuidance(url, apiKey);
421
467
  return await dispatch2(sdkApi.endpoints.deleteRule.initiate({ rulesetId, apiUrl: url, apiKey })).unwrap();
422
- } catch (e) {
423
- return rejectWithValue(e.message || "Failed to delete ruleset");
468
+ } catch (error) {
469
+ return rejectWithValue(extractThunkErrorMessage(error, "Failed to delete ruleset"));
424
470
  }
425
471
  }
426
472
  );
@@ -469,6 +515,124 @@ var bridgeSlice_default = bridgeSlice.reducer;
469
515
 
470
516
  // src/soulSlice.ts
471
517
  import { createSlice as createSlice2, createAsyncThunk as createAsyncThunk2 } from "@reduxjs/toolkit";
518
+
519
+ // src/handlers/arweave.ts
520
+ var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
521
+ var getLocalWalletToken = () => {
522
+ const maybeEnv = globalThis.process?.env;
523
+ return maybeEnv?.ARWEAVE_WALLET_JWK ?? null;
524
+ };
525
+ var withTimeout = async (promiseFactory, timeoutMs = 6e4) => {
526
+ const controller = new AbortController();
527
+ const timeout = setTimeout(() => controller.abort(), timeoutMs);
528
+ try {
529
+ return await promiseFactory(controller.signal);
530
+ } finally {
531
+ clearTimeout(timeout);
532
+ }
533
+ };
534
+ async function handler_ArweaveUpload(instruction, maxRetries = 3) {
535
+ const headers = {
536
+ "Content-Type": instruction.auiContentType || "application/json"
537
+ };
538
+ const authToken = instruction.auiAuthHeader ?? (getLocalWalletToken() ? `Bearer ${getLocalWalletToken()}` : null);
539
+ if (authToken) {
540
+ headers.Authorization = authToken;
541
+ }
542
+ let attempt = 0;
543
+ while (attempt < maxRetries) {
544
+ attempt += 1;
545
+ try {
546
+ const response = await withTimeout(
547
+ (signal) => fetch(instruction.auiEndpoint, {
548
+ method: "POST",
549
+ headers,
550
+ body: JSON.stringify(instruction.auiPayload),
551
+ signal
552
+ })
553
+ );
554
+ let responseBody = null;
555
+ try {
556
+ responseBody = await response.json();
557
+ } catch {
558
+ responseBody = null;
559
+ }
560
+ const txId = responseBody?.id ?? `ar_tx_sdk_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
561
+ const success = response.status >= 200 && response.status < 300;
562
+ if (!success && attempt < maxRetries) {
563
+ await sleep(250 * 2 ** (attempt - 1));
564
+ continue;
565
+ }
566
+ return {
567
+ aurTxId: txId,
568
+ aurStatus: response.status,
569
+ aurSuccess: success,
570
+ aurError: success ? null : `upload_failed_status_${response.status}`
571
+ };
572
+ } catch (error) {
573
+ const message = error instanceof Error ? error.message : String(error);
574
+ if (attempt < maxRetries) {
575
+ await sleep(250 * 2 ** (attempt - 1));
576
+ continue;
577
+ }
578
+ return {
579
+ aurTxId: `ar_tx_failed_${Date.now()}`,
580
+ aurStatus: 0,
581
+ aurSuccess: false,
582
+ aurError: `upload_request_failed:${message}`
583
+ };
584
+ }
585
+ }
586
+ return {
587
+ aurTxId: `ar_tx_failed_${Date.now()}`,
588
+ aurStatus: 0,
589
+ aurSuccess: false,
590
+ aurError: "upload_retry_exhausted"
591
+ };
592
+ }
593
+ async function handler_ArweaveDownload(instruction) {
594
+ try {
595
+ const response = await withTimeout(
596
+ (signal) => fetch(instruction.adiGatewayUrl, {
597
+ method: "GET",
598
+ signal
599
+ })
600
+ );
601
+ if (response.status < 200 || response.status >= 300) {
602
+ return {
603
+ adrBody: null,
604
+ adrStatus: response.status,
605
+ adrSuccess: false,
606
+ adrError: `download_failed_status_${response.status}`
607
+ };
608
+ }
609
+ try {
610
+ const body = await response.json();
611
+ return {
612
+ adrBody: body,
613
+ adrStatus: response.status,
614
+ adrSuccess: true,
615
+ adrError: null
616
+ };
617
+ } catch {
618
+ return {
619
+ adrBody: null,
620
+ adrStatus: response.status,
621
+ adrSuccess: false,
622
+ adrError: "download_invalid_json"
623
+ };
624
+ }
625
+ } catch (error) {
626
+ return {
627
+ adrBody: null,
628
+ adrStatus: 0,
629
+ adrSuccess: false,
630
+ adrError: error instanceof Error ? error.message : String(error)
631
+ };
632
+ }
633
+ }
634
+
635
+ // src/soulSlice.ts
472
636
  var initialState2 = {
473
637
  exportStatus: "idle",
474
638
  importStatus: "idle",
@@ -479,26 +643,45 @@ var initialState2 = {
479
643
  };
480
644
  var remoteExportSoulThunk = createAsyncThunk2(
481
645
  "soul/export",
482
- async ({ npcId: argNpcId, apiUrl, apiKey, memories = [] }, { getState, dispatch: dispatch2, rejectWithValue }) => {
646
+ async ({ npcId: argNpcId, apiUrl, apiKey }, { getState, dispatch: dispatch2, rejectWithValue }) => {
483
647
  try {
484
648
  const state = getState().npc;
485
649
  const npcId = argNpcId || state.activeNpcId;
486
650
  const npc = state.entities[npcId];
487
651
  if (!npc) throw new Error(`NPC ${npcId} not found`);
488
652
  const url = apiUrl || "https://api.forboc.ai";
489
- const result = await dispatch2(sdkApi.endpoints.postSoulExport.initiate({
490
- npcId,
491
- request: { npcIdRef: npcId, persona: npc.persona || "NPC", npcState: npc.state },
492
- apiUrl: url,
493
- apiKey
494
- })).unwrap();
653
+ requireApiKeyGuidance(url, apiKey);
654
+ const phase1 = await dispatch2(
655
+ sdkApi.endpoints.postSoulExport.initiate({
656
+ npcId,
657
+ request: { npcIdRef: npcId, persona: npc.persona || "NPC", npcState: npc.state },
658
+ apiUrl: url,
659
+ apiKey
660
+ })
661
+ ).unwrap();
662
+ const uploadResult = await handler_ArweaveUpload({
663
+ ...phase1.se1Instruction,
664
+ auiAuthHeader: phase1.se1Instruction.auiAuthHeader ?? null
665
+ });
666
+ const final = await dispatch2(
667
+ sdkApi.endpoints.postSoulExportConfirm.initiate({
668
+ npcId,
669
+ request: {
670
+ secUploadResult: uploadResult,
671
+ secSignedPayload: phase1.se1SignedPayload,
672
+ secSignature: phase1.se1Signature
673
+ },
674
+ apiUrl: url,
675
+ apiKey
676
+ })
677
+ ).unwrap();
495
678
  return {
496
- txId: result.txId,
497
- url: result.arweaveUrl,
498
- soul: result.soul
679
+ txId: final.txId,
680
+ url: final.arweaveUrl,
681
+ soul: final.soul
499
682
  };
500
- } catch (e) {
501
- return rejectWithValue(e.message || "Soul export failed");
683
+ } catch (error) {
684
+ return rejectWithValue(extractThunkErrorMessage(error, "Soul export failed"));
502
685
  }
503
686
  }
504
687
  );
@@ -507,10 +690,31 @@ var importSoulFromArweaveThunk = createAsyncThunk2(
507
690
  async ({ txId, apiUrl, apiKey }, { dispatch: dispatch2, rejectWithValue }) => {
508
691
  try {
509
692
  const url = apiUrl || "https://api.forboc.ai";
510
- const data = await dispatch2(sdkApi.endpoints.getSoulImport.initiate({ txId, apiUrl: url, apiKey })).unwrap();
511
- return data;
512
- } catch (e) {
513
- return rejectWithValue(e.message || "Soul import failed");
693
+ requireApiKeyGuidance(url, apiKey);
694
+ const phase1 = await dispatch2(
695
+ sdkApi.endpoints.postNpcImport.initiate({ request: { txIdRef: txId }, apiUrl: url, apiKey })
696
+ ).unwrap();
697
+ const downloadResult = await handler_ArweaveDownload(phase1.si1Instruction);
698
+ const npc = await dispatch2(
699
+ sdkApi.endpoints.postNpcImportConfirm.initiate({
700
+ request: {
701
+ sicTxId: txId,
702
+ sicDownloadResult: downloadResult
703
+ },
704
+ apiUrl: url,
705
+ apiKey
706
+ })
707
+ ).unwrap();
708
+ return {
709
+ id: txId,
710
+ version: "2.0.0",
711
+ name: npc.npcId,
712
+ persona: npc.persona,
713
+ memories: [],
714
+ state: npc.data || {}
715
+ };
716
+ } catch (error) {
717
+ return rejectWithValue(extractThunkErrorMessage(error, "Soul import failed"));
514
718
  }
515
719
  }
516
720
  );
@@ -519,10 +723,11 @@ var getSoulListThunk = createAsyncThunk2(
519
723
  async ({ limit = 50, apiUrl, apiKey }, { dispatch: dispatch2, rejectWithValue }) => {
520
724
  try {
521
725
  const url = apiUrl || "https://api.forboc.ai";
726
+ requireApiKeyGuidance(url, apiKey);
522
727
  const data = await dispatch2(sdkApi.endpoints.getSouls.initiate({ limit, apiUrl: url, apiKey })).unwrap();
523
728
  return data.souls || [];
524
- } catch (e) {
525
- return rejectWithValue(e.message || "Failed to list souls");
729
+ } catch (error) {
730
+ return rejectWithValue(extractThunkErrorMessage(error, "Failed to list souls"));
526
731
  }
527
732
  }
528
733
  );
@@ -531,9 +736,10 @@ var verifySoulThunk = createAsyncThunk2(
531
736
  async ({ txId, apiUrl, apiKey }, { dispatch: dispatch2, rejectWithValue }) => {
532
737
  try {
533
738
  const url = apiUrl || "https://api.forboc.ai";
739
+ requireApiKeyGuidance(url, apiKey);
534
740
  return await dispatch2(sdkApi.endpoints.postSoulVerify.initiate({ txId, apiUrl: url, apiKey })).unwrap();
535
- } catch (e) {
536
- return rejectWithValue(e.message || "Soul verify failed");
741
+ } catch (error) {
742
+ return rejectWithValue(extractThunkErrorMessage(error, "Soul verify failed"));
537
743
  }
538
744
  }
539
745
  );
@@ -542,13 +748,24 @@ var importNpcFromSoulThunk = createAsyncThunk2(
542
748
  async ({ txId, apiUrl, apiKey }, { dispatch: dispatch2, rejectWithValue }) => {
543
749
  try {
544
750
  const url = apiUrl || "https://api.forboc.ai";
545
- return await dispatch2(sdkApi.endpoints.postNpcImport.initiate({
546
- request: { txIdRef: txId },
547
- apiUrl: url,
548
- apiKey
549
- })).unwrap();
550
- } catch (e) {
551
- return rejectWithValue(e.message || "NPC import from soul failed");
751
+ requireApiKeyGuidance(url, apiKey);
752
+ const phase1 = await dispatch2(
753
+ sdkApi.endpoints.postNpcImport.initiate({
754
+ request: { txIdRef: txId },
755
+ apiUrl: url,
756
+ apiKey
757
+ })
758
+ ).unwrap();
759
+ const downloadResult = await handler_ArweaveDownload(phase1.si1Instruction);
760
+ return await dispatch2(
761
+ sdkApi.endpoints.postNpcImportConfirm.initiate({
762
+ request: { sicTxId: txId, sicDownloadResult: downloadResult },
763
+ apiUrl: url,
764
+ apiKey
765
+ })
766
+ ).unwrap();
767
+ } catch (error) {
768
+ return rejectWithValue(extractThunkErrorMessage(error, "NPC import from soul failed"));
552
769
  }
553
770
  }
554
771
  );
@@ -607,6 +824,7 @@ var startGhostThunk = createAsyncThunk3(
607
824
  async (config, { dispatch: dispatch2, rejectWithValue }) => {
608
825
  try {
609
826
  const apiUrl = config.apiUrl || "https://api.forboc.ai";
827
+ requireApiKeyGuidance(apiUrl, config.apiKey);
610
828
  const data = await dispatch2(sdkApi.endpoints.postGhostRun.initiate({
611
829
  request: { testSuite: config.testSuite, duration: config.duration ?? 300 },
612
830
  apiUrl,
@@ -616,8 +834,8 @@ var startGhostThunk = createAsyncThunk3(
616
834
  sessionId: data.sessionId,
617
835
  status: data.runStatus
618
836
  };
619
- } catch (e) {
620
- return rejectWithValue(e.message || "Failed to start Ghost");
837
+ } catch (error) {
838
+ return rejectWithValue(extractThunkErrorMessage(error, "Failed to start Ghost"));
621
839
  }
622
840
  }
623
841
  );
@@ -629,6 +847,7 @@ var getGhostStatusThunk = createAsyncThunk3(
629
847
  const targetSession = sessionId || state.activeSessionId;
630
848
  if (!targetSession) throw new Error("No active Ghost session");
631
849
  const url = apiUrl || "https://api.forboc.ai";
850
+ requireApiKeyGuidance(url, apiKey);
632
851
  const data = await dispatch2(sdkApi.endpoints.getGhostStatus.initiate({ sessionId: targetSession, apiUrl: url, apiKey })).unwrap();
633
852
  return {
634
853
  sessionId: data.ghostSessionId,
@@ -638,8 +857,8 @@ var getGhostStatusThunk = createAsyncThunk3(
638
857
  duration: data.ghostDuration || 0,
639
858
  errors: data.ghostErrors
640
859
  };
641
- } catch (e) {
642
- return rejectWithValue(e.message || "Failed to get ghost status");
860
+ } catch (error) {
861
+ return rejectWithValue(extractThunkErrorMessage(error, "Failed to get ghost status"));
643
862
  }
644
863
  }
645
864
  );
@@ -651,6 +870,7 @@ var getGhostResultsThunk = createAsyncThunk3(
651
870
  const targetSession = sessionId || state.activeSessionId;
652
871
  if (!targetSession) throw new Error("No active Ghost session");
653
872
  const url = apiUrl || "https://api.forboc.ai";
873
+ requireApiKeyGuidance(url, apiKey);
654
874
  const data = await dispatch2(sdkApi.endpoints.getGhostResults.initiate({ sessionId: targetSession, apiUrl: url, apiKey })).unwrap();
655
875
  return {
656
876
  sessionId: data.resultsSessionId,
@@ -669,8 +889,8 @@ var getGhostResultsThunk = createAsyncThunk3(
669
889
  coverage: data.resultsCoverage,
670
890
  metrics: Object.fromEntries(data.resultsMetrics || [])
671
891
  };
672
- } catch (e) {
673
- return rejectWithValue(e.message || "Failed to get ghost results");
892
+ } catch (error) {
893
+ return rejectWithValue(extractThunkErrorMessage(error, "Failed to get ghost results"));
674
894
  }
675
895
  }
676
896
  );
@@ -682,14 +902,15 @@ var stopGhostThunk = createAsyncThunk3(
682
902
  const targetSession = sessionId || state.activeSessionId;
683
903
  if (!targetSession) throw new Error("No active Ghost session");
684
904
  const url = apiUrl || "https://api.forboc.ai";
905
+ requireApiKeyGuidance(url, apiKey);
685
906
  const data = await dispatch2(sdkApi.endpoints.postGhostStop.initiate({ sessionId: targetSession, apiUrl: url, apiKey })).unwrap();
686
907
  return {
687
908
  stopped: data.stopped,
688
909
  status: data.stopStatus,
689
910
  sessionId: data.stopSessionId
690
911
  };
691
- } catch (e) {
692
- return rejectWithValue(e.message || "Failed to stop ghost session");
912
+ } catch (error) {
913
+ return rejectWithValue(extractThunkErrorMessage(error, "Failed to stop ghost session"));
693
914
  }
694
915
  }
695
916
  );
@@ -698,6 +919,7 @@ var getGhostHistoryThunk = createAsyncThunk3(
698
919
  async ({ limit = 10, apiUrl, apiKey }, { dispatch: dispatch2, rejectWithValue }) => {
699
920
  try {
700
921
  const url = apiUrl || "https://api.forboc.ai";
922
+ requireApiKeyGuidance(url, apiKey);
701
923
  const data = await dispatch2(sdkApi.endpoints.getGhostHistory.initiate({ limit, apiUrl: url, apiKey })).unwrap();
702
924
  return (data.sessions || []).map((s) => ({
703
925
  sessionId: s.sessionId,
@@ -707,8 +929,8 @@ var getGhostHistoryThunk = createAsyncThunk3(
707
929
  status: s.status,
708
930
  passRate: s.passRate
709
931
  }));
710
- } catch (e) {
711
- return rejectWithValue(e.message || "Failed to get ghost history");
932
+ } catch (error) {
933
+ return rejectWithValue(extractThunkErrorMessage(error, "Failed to get ghost history"));
712
934
  }
713
935
  }
714
936
  );
@@ -743,7 +965,7 @@ var ghostSlice = createSlice3({
743
965
  state.status = "completed";
744
966
  }).addCase(stopGhostThunk.fulfilled, (state, action) => {
745
967
  if (action.payload.stopped) {
746
- state.status = "failed";
968
+ state.status = "completed";
747
969
  } else {
748
970
  state.error = action.payload.status || "Ghost stop request did not stop a session";
749
971
  }
@@ -839,7 +1061,8 @@ var npcSlice = createSlice4({
839
1061
  persona,
840
1062
  state: initialState5 || {},
841
1063
  history: [],
842
- isBlocked: false
1064
+ isBlocked: false,
1065
+ stateLog: [{ timestamp: Date.now(), delta: initialState5 || {}, state: initialState5 || {} }]
843
1066
  });
844
1067
  state.activeNpcId = id;
845
1068
  },
@@ -854,7 +1077,9 @@ var npcSlice = createSlice4({
854
1077
  const { id, delta } = action.payload;
855
1078
  const npc = state.entities[id];
856
1079
  if (npc) {
857
- npc.state = { ...npc.state, ...delta };
1080
+ const newState = { ...npc.state, ...delta };
1081
+ npc.state = newState;
1082
+ npc.stateLog.push({ timestamp: Date.now(), delta, state: newState });
858
1083
  }
859
1084
  },
860
1085
  addToHistory: (state, action) => {
@@ -924,6 +1149,7 @@ var initRemoteCortexThunk = createAsyncThunk4(
924
1149
  async ({ model = "api-integrated", authKey, apiUrl, apiKey }, { dispatch: dispatch2, rejectWithValue }) => {
925
1150
  try {
926
1151
  const url = apiUrl || "https://api.forboc.ai";
1152
+ requireApiKeyGuidance(url, apiKey);
927
1153
  const data = await dispatch2(sdkApi.endpoints.postCortexInit.initiate({
928
1154
  request: { requestedModel: model, authKey },
929
1155
  apiUrl: url,
@@ -935,8 +1161,8 @@ var initRemoteCortexThunk = createAsyncThunk4(
935
1161
  ready: data.state?.toLowerCase() === "ready",
936
1162
  engine: "remote"
937
1163
  };
938
- } catch (e) {
939
- return rejectWithValue(e.message || "Remote cortex init failed");
1164
+ } catch (error) {
1165
+ return rejectWithValue(extractThunkErrorMessage(error, "Remote cortex init failed"));
940
1166
  }
941
1167
  }
942
1168
  );
@@ -945,9 +1171,10 @@ var listCortexModelsThunk = createAsyncThunk4(
945
1171
  async ({ apiUrl, apiKey }, { dispatch: dispatch2, rejectWithValue }) => {
946
1172
  try {
947
1173
  const url = apiUrl || "https://api.forboc.ai";
1174
+ requireApiKeyGuidance(url, apiKey);
948
1175
  return await dispatch2(sdkApi.endpoints.getCortexModels.initiate({ apiUrl: url, apiKey })).unwrap();
949
- } catch (e) {
950
- return rejectWithValue(e.message || "Failed to list cortex models");
1176
+ } catch (error) {
1177
+ return rejectWithValue(extractThunkErrorMessage(error, "Failed to list cortex models"));
951
1178
  }
952
1179
  }
953
1180
  );
@@ -955,6 +1182,7 @@ var completeRemoteThunk = createAsyncThunk4(
955
1182
  "cortex/completeRemote",
956
1183
  async ({ cortexId, prompt, options, apiUrl, apiKey }, { dispatch: dispatch2, rejectWithValue }) => {
957
1184
  try {
1185
+ requireApiKeyGuidance(apiUrl, apiKey);
958
1186
  const data = await dispatch2(sdkApi.endpoints.postCortexComplete.initiate({
959
1187
  cortexId,
960
1188
  prompt,
@@ -963,8 +1191,8 @@ var completeRemoteThunk = createAsyncThunk4(
963
1191
  apiKey
964
1192
  })).unwrap();
965
1193
  return data.text;
966
- } catch (e) {
967
- return rejectWithValue(e.message || "Remote completing failed");
1194
+ } catch (error) {
1195
+ return rejectWithValue(extractThunkErrorMessage(error, "Remote completing failed"));
968
1196
  }
969
1197
  }
970
1198
  );
@@ -1199,7 +1427,7 @@ var dispatch = store.dispatch;
1199
1427
 
1200
1428
  // src/thunks.ts
1201
1429
  import { createAsyncThunk as createAsyncThunk5 } from "@reduxjs/toolkit";
1202
- var extractThunkErrorMessage = (e) => {
1430
+ var extractThunkErrorMessage2 = (e) => {
1203
1431
  if (typeof e === "string") return e;
1204
1432
  if (e?.data?.message) return String(e.data.message);
1205
1433
  if (e?.error) return String(e.error);
@@ -1333,7 +1561,7 @@ var processNPC = createAsyncThunk5(
1333
1561
  };
1334
1562
  return runProtocolRecursively(initialTape, void 0, 0);
1335
1563
  } catch (e) {
1336
- const message = extractThunkErrorMessage(e);
1564
+ const message = extractThunkErrorMessage2(e);
1337
1565
  dispatch2(directiveRunFailed({ id: directiveId, error: String(message) }));
1338
1566
  return rejectWithValue(String(message));
1339
1567
  }
@@ -1453,6 +1681,8 @@ export {
1453
1681
  getGhostStatusThunk,
1454
1682
  getSoulListThunk,
1455
1683
  ghostSlice,
1684
+ handler_ArweaveDownload,
1685
+ handler_ArweaveUpload,
1456
1686
  importNpcFromSoulThunk,
1457
1687
  importSoulFromArweaveThunk,
1458
1688
  initRemoteCortexThunk,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forbocai/core",
3
- "version": "0.6.2",
3
+ "version": "0.6.3",
4
4
  "license": "UNLICENSED",
5
5
  "description": "Core agnostic logic for ForbocAI SDK",
6
6
  "main": "dist/index.js",