@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.js CHANGED
@@ -54,6 +54,8 @@ __export(index_exports, {
54
54
  getGhostStatusThunk: () => getGhostStatusThunk,
55
55
  getSoulListThunk: () => getSoulListThunk,
56
56
  ghostSlice: () => ghostSlice,
57
+ handler_ArweaveDownload: () => handler_ArweaveDownload,
58
+ handler_ArweaveUpload: () => handler_ArweaveUpload,
57
59
  importNpcFromSoulThunk: () => importNpcFromSoulThunk,
58
60
  importSoulFromArweaveThunk: () => importSoulFromArweaveThunk,
59
61
  initRemoteCortexThunk: () => initRemoteCortexThunk,
@@ -325,6 +327,16 @@ var sdkApi = (0, import_query.createApi)({
325
327
  invalidatesTags: ["Soul"],
326
328
  transformResponse: (response) => response
327
329
  }),
330
+ postSoulExportConfirm: builder.mutation({
331
+ query: ({ npcId, request, apiUrl, apiKey }) => ({
332
+ url: `${apiUrl}/npcs/${npcId}/soul/confirm`,
333
+ method: "POST",
334
+ headers: apiKey ? { "Authorization": `Bearer ${apiKey}` } : {},
335
+ body: request
336
+ }),
337
+ invalidatesTags: ["Soul"],
338
+ transformResponse: (response) => response
339
+ }),
328
340
  getSoulImport: builder.query({
329
341
  query: ({ txId, apiUrl, apiKey }) => ({
330
342
  url: `${apiUrl}/souls/${txId}`,
@@ -433,6 +445,16 @@ var sdkApi = (0, import_query.createApi)({
433
445
  invalidatesTags: ["NPC"],
434
446
  transformResponse: (response) => response
435
447
  }),
448
+ postNpcImportConfirm: builder.mutation({
449
+ query: ({ request, apiUrl, apiKey }) => ({
450
+ url: `${apiUrl}/npcs/import/confirm`,
451
+ method: "POST",
452
+ headers: apiKey ? { "Authorization": `Bearer ${apiKey}` } : {},
453
+ body: request
454
+ }),
455
+ invalidatesTags: ["NPC"],
456
+ transformResponse: (response) => response
457
+ }),
436
458
  // Cortex Remote Endpoint
437
459
  postCortexComplete: builder.mutation({
438
460
  query: ({ cortexId, prompt, options, apiUrl, apiKey }) => ({
@@ -452,6 +474,25 @@ var sdkApi = (0, import_query.createApi)({
452
474
  })
453
475
  });
454
476
 
477
+ // src/errors.ts
478
+ var extractThunkErrorMessage = (error, fallback) => {
479
+ if (typeof error === "string") return error;
480
+ if (error && typeof error === "object") {
481
+ const e = error;
482
+ if (typeof e.data === "object" && e.data?.message) return String(e.data.message);
483
+ if (typeof e.data === "string") return e.data;
484
+ if (e.message) return e.message;
485
+ if (e.error) return e.error;
486
+ }
487
+ return fallback;
488
+ };
489
+ var requireApiKeyGuidance = (apiUrl, apiKey) => {
490
+ const normalized = apiUrl.toLowerCase();
491
+ if (normalized.includes("api.forboc.ai") && !apiKey) {
492
+ throw new Error("Missing API key. Set FORBOCAI_API_KEY (or run `forboc config set apiKey <key>`) for production API calls.");
493
+ }
494
+ };
495
+
455
496
  // src/bridgeSlice.ts
456
497
  var initialState = {
457
498
  activePresets: [],
@@ -466,6 +507,7 @@ var validateBridgeThunk = (0, import_toolkit.createAsyncThunk)(
466
507
  async ({ action, context, npcId, apiUrl, apiKey }, { dispatch: dispatch2, rejectWithValue }) => {
467
508
  try {
468
509
  const url = apiUrl || "https://api.forboc.ai";
510
+ requireApiKeyGuidance(url, apiKey);
469
511
  const data = await dispatch2(sdkApi.endpoints.postBridgeValidate.initiate({
470
512
  request: { action, context },
471
513
  npcId,
@@ -473,8 +515,8 @@ var validateBridgeThunk = (0, import_toolkit.createAsyncThunk)(
473
515
  apiKey
474
516
  })).unwrap();
475
517
  return data;
476
- } catch (e) {
477
- return rejectWithValue(e.message || "Bridge validation failed");
518
+ } catch (error) {
519
+ return rejectWithValue(extractThunkErrorMessage(error, "Bridge validation failed"));
478
520
  }
479
521
  }
480
522
  );
@@ -483,9 +525,10 @@ var loadBridgePresetThunk = (0, import_toolkit.createAsyncThunk)(
483
525
  async ({ presetName, apiUrl, apiKey }, { dispatch: dispatch2, rejectWithValue }) => {
484
526
  try {
485
527
  const url = apiUrl || "https://api.forboc.ai";
528
+ requireApiKeyGuidance(url, apiKey);
486
529
  return await dispatch2(sdkApi.endpoints.postBridgePreset.initiate({ presetName, apiUrl: url, apiKey })).unwrap();
487
- } catch (e) {
488
- return rejectWithValue(e.message || "Failed to load preset");
530
+ } catch (error) {
531
+ return rejectWithValue(extractThunkErrorMessage(error, "Failed to load preset"));
489
532
  }
490
533
  }
491
534
  );
@@ -494,9 +537,10 @@ var getBridgeRulesThunk = (0, import_toolkit.createAsyncThunk)(
494
537
  async ({ apiUrl, apiKey }, { dispatch: dispatch2, rejectWithValue }) => {
495
538
  try {
496
539
  const url = apiUrl || "https://api.forboc.ai";
540
+ requireApiKeyGuidance(url, apiKey);
497
541
  return await dispatch2(sdkApi.endpoints.getBridgeRules.initiate({ apiUrl: url, apiKey })).unwrap();
498
- } catch (e) {
499
- return rejectWithValue(e.message || "Failed to list bridge rules");
542
+ } catch (error) {
543
+ return rejectWithValue(extractThunkErrorMessage(error, "Failed to list bridge rules"));
500
544
  }
501
545
  }
502
546
  );
@@ -505,9 +549,10 @@ var listRulesetsThunk = (0, import_toolkit.createAsyncThunk)(
505
549
  async ({ apiUrl, apiKey }, { dispatch: dispatch2, rejectWithValue }) => {
506
550
  try {
507
551
  const url = apiUrl || "https://api.forboc.ai";
552
+ requireApiKeyGuidance(url, apiKey);
508
553
  return await dispatch2(sdkApi.endpoints.getRulesets.initiate({ apiUrl: url, apiKey })).unwrap();
509
- } catch (e) {
510
- return rejectWithValue(e.message || "Failed to list rulesets");
554
+ } catch (error) {
555
+ return rejectWithValue(extractThunkErrorMessage(error, "Failed to list rulesets"));
511
556
  }
512
557
  }
513
558
  );
@@ -516,9 +561,10 @@ var listRulePresetsThunk = (0, import_toolkit.createAsyncThunk)(
516
561
  async ({ apiUrl, apiKey }, { dispatch: dispatch2, rejectWithValue }) => {
517
562
  try {
518
563
  const url = apiUrl || "https://api.forboc.ai";
564
+ requireApiKeyGuidance(url, apiKey);
519
565
  return await dispatch2(sdkApi.endpoints.getRulePresets.initiate({ apiUrl: url, apiKey })).unwrap();
520
- } catch (e) {
521
- return rejectWithValue(e.message || "Failed to list rule presets");
566
+ } catch (error) {
567
+ return rejectWithValue(extractThunkErrorMessage(error, "Failed to list rule presets"));
522
568
  }
523
569
  }
524
570
  );
@@ -527,9 +573,10 @@ var registerRulesetThunk = (0, import_toolkit.createAsyncThunk)(
527
573
  async ({ ruleset, apiUrl, apiKey }, { dispatch: dispatch2, rejectWithValue }) => {
528
574
  try {
529
575
  const url = apiUrl || "https://api.forboc.ai";
576
+ requireApiKeyGuidance(url, apiKey);
530
577
  return await dispatch2(sdkApi.endpoints.postRuleRegister.initiate({ request: ruleset, apiUrl: url, apiKey })).unwrap();
531
- } catch (e) {
532
- return rejectWithValue(e.message || "Failed to register ruleset");
578
+ } catch (error) {
579
+ return rejectWithValue(extractThunkErrorMessage(error, "Failed to register ruleset"));
533
580
  }
534
581
  }
535
582
  );
@@ -538,9 +585,10 @@ var deleteRulesetThunk = (0, import_toolkit.createAsyncThunk)(
538
585
  async ({ rulesetId, apiUrl, apiKey }, { dispatch: dispatch2, rejectWithValue }) => {
539
586
  try {
540
587
  const url = apiUrl || "https://api.forboc.ai";
588
+ requireApiKeyGuidance(url, apiKey);
541
589
  return await dispatch2(sdkApi.endpoints.deleteRule.initiate({ rulesetId, apiUrl: url, apiKey })).unwrap();
542
- } catch (e) {
543
- return rejectWithValue(e.message || "Failed to delete ruleset");
590
+ } catch (error) {
591
+ return rejectWithValue(extractThunkErrorMessage(error, "Failed to delete ruleset"));
544
592
  }
545
593
  }
546
594
  );
@@ -589,6 +637,124 @@ var bridgeSlice_default = bridgeSlice.reducer;
589
637
 
590
638
  // src/soulSlice.ts
591
639
  var import_toolkit2 = require("@reduxjs/toolkit");
640
+
641
+ // src/handlers/arweave.ts
642
+ var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
643
+ var getLocalWalletToken = () => {
644
+ const maybeEnv = globalThis.process?.env;
645
+ return maybeEnv?.ARWEAVE_WALLET_JWK ?? null;
646
+ };
647
+ var withTimeout = async (promiseFactory, timeoutMs = 6e4) => {
648
+ const controller = new AbortController();
649
+ const timeout = setTimeout(() => controller.abort(), timeoutMs);
650
+ try {
651
+ return await promiseFactory(controller.signal);
652
+ } finally {
653
+ clearTimeout(timeout);
654
+ }
655
+ };
656
+ async function handler_ArweaveUpload(instruction, maxRetries = 3) {
657
+ const headers = {
658
+ "Content-Type": instruction.auiContentType || "application/json"
659
+ };
660
+ const authToken = instruction.auiAuthHeader ?? (getLocalWalletToken() ? `Bearer ${getLocalWalletToken()}` : null);
661
+ if (authToken) {
662
+ headers.Authorization = authToken;
663
+ }
664
+ let attempt = 0;
665
+ while (attempt < maxRetries) {
666
+ attempt += 1;
667
+ try {
668
+ const response = await withTimeout(
669
+ (signal) => fetch(instruction.auiEndpoint, {
670
+ method: "POST",
671
+ headers,
672
+ body: JSON.stringify(instruction.auiPayload),
673
+ signal
674
+ })
675
+ );
676
+ let responseBody = null;
677
+ try {
678
+ responseBody = await response.json();
679
+ } catch {
680
+ responseBody = null;
681
+ }
682
+ const txId = responseBody?.id ?? `ar_tx_sdk_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
683
+ const success = response.status >= 200 && response.status < 300;
684
+ if (!success && attempt < maxRetries) {
685
+ await sleep(250 * 2 ** (attempt - 1));
686
+ continue;
687
+ }
688
+ return {
689
+ aurTxId: txId,
690
+ aurStatus: response.status,
691
+ aurSuccess: success,
692
+ aurError: success ? null : `upload_failed_status_${response.status}`
693
+ };
694
+ } catch (error) {
695
+ const message = error instanceof Error ? error.message : String(error);
696
+ if (attempt < maxRetries) {
697
+ await sleep(250 * 2 ** (attempt - 1));
698
+ continue;
699
+ }
700
+ return {
701
+ aurTxId: `ar_tx_failed_${Date.now()}`,
702
+ aurStatus: 0,
703
+ aurSuccess: false,
704
+ aurError: `upload_request_failed:${message}`
705
+ };
706
+ }
707
+ }
708
+ return {
709
+ aurTxId: `ar_tx_failed_${Date.now()}`,
710
+ aurStatus: 0,
711
+ aurSuccess: false,
712
+ aurError: "upload_retry_exhausted"
713
+ };
714
+ }
715
+ async function handler_ArweaveDownload(instruction) {
716
+ try {
717
+ const response = await withTimeout(
718
+ (signal) => fetch(instruction.adiGatewayUrl, {
719
+ method: "GET",
720
+ signal
721
+ })
722
+ );
723
+ if (response.status < 200 || response.status >= 300) {
724
+ return {
725
+ adrBody: null,
726
+ adrStatus: response.status,
727
+ adrSuccess: false,
728
+ adrError: `download_failed_status_${response.status}`
729
+ };
730
+ }
731
+ try {
732
+ const body = await response.json();
733
+ return {
734
+ adrBody: body,
735
+ adrStatus: response.status,
736
+ adrSuccess: true,
737
+ adrError: null
738
+ };
739
+ } catch {
740
+ return {
741
+ adrBody: null,
742
+ adrStatus: response.status,
743
+ adrSuccess: false,
744
+ adrError: "download_invalid_json"
745
+ };
746
+ }
747
+ } catch (error) {
748
+ return {
749
+ adrBody: null,
750
+ adrStatus: 0,
751
+ adrSuccess: false,
752
+ adrError: error instanceof Error ? error.message : String(error)
753
+ };
754
+ }
755
+ }
756
+
757
+ // src/soulSlice.ts
592
758
  var initialState2 = {
593
759
  exportStatus: "idle",
594
760
  importStatus: "idle",
@@ -599,26 +765,45 @@ var initialState2 = {
599
765
  };
600
766
  var remoteExportSoulThunk = (0, import_toolkit2.createAsyncThunk)(
601
767
  "soul/export",
602
- async ({ npcId: argNpcId, apiUrl, apiKey, memories = [] }, { getState, dispatch: dispatch2, rejectWithValue }) => {
768
+ async ({ npcId: argNpcId, apiUrl, apiKey }, { getState, dispatch: dispatch2, rejectWithValue }) => {
603
769
  try {
604
770
  const state = getState().npc;
605
771
  const npcId = argNpcId || state.activeNpcId;
606
772
  const npc = state.entities[npcId];
607
773
  if (!npc) throw new Error(`NPC ${npcId} not found`);
608
774
  const url = apiUrl || "https://api.forboc.ai";
609
- const result = await dispatch2(sdkApi.endpoints.postSoulExport.initiate({
610
- npcId,
611
- request: { npcIdRef: npcId, persona: npc.persona || "NPC", npcState: npc.state },
612
- apiUrl: url,
613
- apiKey
614
- })).unwrap();
775
+ requireApiKeyGuidance(url, apiKey);
776
+ const phase1 = await dispatch2(
777
+ sdkApi.endpoints.postSoulExport.initiate({
778
+ npcId,
779
+ request: { npcIdRef: npcId, persona: npc.persona || "NPC", npcState: npc.state },
780
+ apiUrl: url,
781
+ apiKey
782
+ })
783
+ ).unwrap();
784
+ const uploadResult = await handler_ArweaveUpload({
785
+ ...phase1.se1Instruction,
786
+ auiAuthHeader: phase1.se1Instruction.auiAuthHeader ?? null
787
+ });
788
+ const final = await dispatch2(
789
+ sdkApi.endpoints.postSoulExportConfirm.initiate({
790
+ npcId,
791
+ request: {
792
+ secUploadResult: uploadResult,
793
+ secSignedPayload: phase1.se1SignedPayload,
794
+ secSignature: phase1.se1Signature
795
+ },
796
+ apiUrl: url,
797
+ apiKey
798
+ })
799
+ ).unwrap();
615
800
  return {
616
- txId: result.txId,
617
- url: result.arweaveUrl,
618
- soul: result.soul
801
+ txId: final.txId,
802
+ url: final.arweaveUrl,
803
+ soul: final.soul
619
804
  };
620
- } catch (e) {
621
- return rejectWithValue(e.message || "Soul export failed");
805
+ } catch (error) {
806
+ return rejectWithValue(extractThunkErrorMessage(error, "Soul export failed"));
622
807
  }
623
808
  }
624
809
  );
@@ -627,10 +812,31 @@ var importSoulFromArweaveThunk = (0, import_toolkit2.createAsyncThunk)(
627
812
  async ({ txId, apiUrl, apiKey }, { dispatch: dispatch2, rejectWithValue }) => {
628
813
  try {
629
814
  const url = apiUrl || "https://api.forboc.ai";
630
- const data = await dispatch2(sdkApi.endpoints.getSoulImport.initiate({ txId, apiUrl: url, apiKey })).unwrap();
631
- return data;
632
- } catch (e) {
633
- return rejectWithValue(e.message || "Soul import failed");
815
+ requireApiKeyGuidance(url, apiKey);
816
+ const phase1 = await dispatch2(
817
+ sdkApi.endpoints.postNpcImport.initiate({ request: { txIdRef: txId }, apiUrl: url, apiKey })
818
+ ).unwrap();
819
+ const downloadResult = await handler_ArweaveDownload(phase1.si1Instruction);
820
+ const npc = await dispatch2(
821
+ sdkApi.endpoints.postNpcImportConfirm.initiate({
822
+ request: {
823
+ sicTxId: txId,
824
+ sicDownloadResult: downloadResult
825
+ },
826
+ apiUrl: url,
827
+ apiKey
828
+ })
829
+ ).unwrap();
830
+ return {
831
+ id: txId,
832
+ version: "2.0.0",
833
+ name: npc.npcId,
834
+ persona: npc.persona,
835
+ memories: [],
836
+ state: npc.data || {}
837
+ };
838
+ } catch (error) {
839
+ return rejectWithValue(extractThunkErrorMessage(error, "Soul import failed"));
634
840
  }
635
841
  }
636
842
  );
@@ -639,10 +845,11 @@ var getSoulListThunk = (0, import_toolkit2.createAsyncThunk)(
639
845
  async ({ limit = 50, apiUrl, apiKey }, { dispatch: dispatch2, rejectWithValue }) => {
640
846
  try {
641
847
  const url = apiUrl || "https://api.forboc.ai";
848
+ requireApiKeyGuidance(url, apiKey);
642
849
  const data = await dispatch2(sdkApi.endpoints.getSouls.initiate({ limit, apiUrl: url, apiKey })).unwrap();
643
850
  return data.souls || [];
644
- } catch (e) {
645
- return rejectWithValue(e.message || "Failed to list souls");
851
+ } catch (error) {
852
+ return rejectWithValue(extractThunkErrorMessage(error, "Failed to list souls"));
646
853
  }
647
854
  }
648
855
  );
@@ -651,9 +858,10 @@ var verifySoulThunk = (0, import_toolkit2.createAsyncThunk)(
651
858
  async ({ txId, apiUrl, apiKey }, { dispatch: dispatch2, rejectWithValue }) => {
652
859
  try {
653
860
  const url = apiUrl || "https://api.forboc.ai";
861
+ requireApiKeyGuidance(url, apiKey);
654
862
  return await dispatch2(sdkApi.endpoints.postSoulVerify.initiate({ txId, apiUrl: url, apiKey })).unwrap();
655
- } catch (e) {
656
- return rejectWithValue(e.message || "Soul verify failed");
863
+ } catch (error) {
864
+ return rejectWithValue(extractThunkErrorMessage(error, "Soul verify failed"));
657
865
  }
658
866
  }
659
867
  );
@@ -662,13 +870,24 @@ var importNpcFromSoulThunk = (0, import_toolkit2.createAsyncThunk)(
662
870
  async ({ txId, apiUrl, apiKey }, { dispatch: dispatch2, rejectWithValue }) => {
663
871
  try {
664
872
  const url = apiUrl || "https://api.forboc.ai";
665
- return await dispatch2(sdkApi.endpoints.postNpcImport.initiate({
666
- request: { txIdRef: txId },
667
- apiUrl: url,
668
- apiKey
669
- })).unwrap();
670
- } catch (e) {
671
- return rejectWithValue(e.message || "NPC import from soul failed");
873
+ requireApiKeyGuidance(url, apiKey);
874
+ const phase1 = await dispatch2(
875
+ sdkApi.endpoints.postNpcImport.initiate({
876
+ request: { txIdRef: txId },
877
+ apiUrl: url,
878
+ apiKey
879
+ })
880
+ ).unwrap();
881
+ const downloadResult = await handler_ArweaveDownload(phase1.si1Instruction);
882
+ return await dispatch2(
883
+ sdkApi.endpoints.postNpcImportConfirm.initiate({
884
+ request: { sicTxId: txId, sicDownloadResult: downloadResult },
885
+ apiUrl: url,
886
+ apiKey
887
+ })
888
+ ).unwrap();
889
+ } catch (error) {
890
+ return rejectWithValue(extractThunkErrorMessage(error, "NPC import from soul failed"));
672
891
  }
673
892
  }
674
893
  );
@@ -727,6 +946,7 @@ var startGhostThunk = (0, import_toolkit3.createAsyncThunk)(
727
946
  async (config, { dispatch: dispatch2, rejectWithValue }) => {
728
947
  try {
729
948
  const apiUrl = config.apiUrl || "https://api.forboc.ai";
949
+ requireApiKeyGuidance(apiUrl, config.apiKey);
730
950
  const data = await dispatch2(sdkApi.endpoints.postGhostRun.initiate({
731
951
  request: { testSuite: config.testSuite, duration: config.duration ?? 300 },
732
952
  apiUrl,
@@ -736,8 +956,8 @@ var startGhostThunk = (0, import_toolkit3.createAsyncThunk)(
736
956
  sessionId: data.sessionId,
737
957
  status: data.runStatus
738
958
  };
739
- } catch (e) {
740
- return rejectWithValue(e.message || "Failed to start Ghost");
959
+ } catch (error) {
960
+ return rejectWithValue(extractThunkErrorMessage(error, "Failed to start Ghost"));
741
961
  }
742
962
  }
743
963
  );
@@ -749,6 +969,7 @@ var getGhostStatusThunk = (0, import_toolkit3.createAsyncThunk)(
749
969
  const targetSession = sessionId || state.activeSessionId;
750
970
  if (!targetSession) throw new Error("No active Ghost session");
751
971
  const url = apiUrl || "https://api.forboc.ai";
972
+ requireApiKeyGuidance(url, apiKey);
752
973
  const data = await dispatch2(sdkApi.endpoints.getGhostStatus.initiate({ sessionId: targetSession, apiUrl: url, apiKey })).unwrap();
753
974
  return {
754
975
  sessionId: data.ghostSessionId,
@@ -758,8 +979,8 @@ var getGhostStatusThunk = (0, import_toolkit3.createAsyncThunk)(
758
979
  duration: data.ghostDuration || 0,
759
980
  errors: data.ghostErrors
760
981
  };
761
- } catch (e) {
762
- return rejectWithValue(e.message || "Failed to get ghost status");
982
+ } catch (error) {
983
+ return rejectWithValue(extractThunkErrorMessage(error, "Failed to get ghost status"));
763
984
  }
764
985
  }
765
986
  );
@@ -771,6 +992,7 @@ var getGhostResultsThunk = (0, import_toolkit3.createAsyncThunk)(
771
992
  const targetSession = sessionId || state.activeSessionId;
772
993
  if (!targetSession) throw new Error("No active Ghost session");
773
994
  const url = apiUrl || "https://api.forboc.ai";
995
+ requireApiKeyGuidance(url, apiKey);
774
996
  const data = await dispatch2(sdkApi.endpoints.getGhostResults.initiate({ sessionId: targetSession, apiUrl: url, apiKey })).unwrap();
775
997
  return {
776
998
  sessionId: data.resultsSessionId,
@@ -789,8 +1011,8 @@ var getGhostResultsThunk = (0, import_toolkit3.createAsyncThunk)(
789
1011
  coverage: data.resultsCoverage,
790
1012
  metrics: Object.fromEntries(data.resultsMetrics || [])
791
1013
  };
792
- } catch (e) {
793
- return rejectWithValue(e.message || "Failed to get ghost results");
1014
+ } catch (error) {
1015
+ return rejectWithValue(extractThunkErrorMessage(error, "Failed to get ghost results"));
794
1016
  }
795
1017
  }
796
1018
  );
@@ -802,14 +1024,15 @@ var stopGhostThunk = (0, import_toolkit3.createAsyncThunk)(
802
1024
  const targetSession = sessionId || state.activeSessionId;
803
1025
  if (!targetSession) throw new Error("No active Ghost session");
804
1026
  const url = apiUrl || "https://api.forboc.ai";
1027
+ requireApiKeyGuidance(url, apiKey);
805
1028
  const data = await dispatch2(sdkApi.endpoints.postGhostStop.initiate({ sessionId: targetSession, apiUrl: url, apiKey })).unwrap();
806
1029
  return {
807
1030
  stopped: data.stopped,
808
1031
  status: data.stopStatus,
809
1032
  sessionId: data.stopSessionId
810
1033
  };
811
- } catch (e) {
812
- return rejectWithValue(e.message || "Failed to stop ghost session");
1034
+ } catch (error) {
1035
+ return rejectWithValue(extractThunkErrorMessage(error, "Failed to stop ghost session"));
813
1036
  }
814
1037
  }
815
1038
  );
@@ -818,6 +1041,7 @@ var getGhostHistoryThunk = (0, import_toolkit3.createAsyncThunk)(
818
1041
  async ({ limit = 10, apiUrl, apiKey }, { dispatch: dispatch2, rejectWithValue }) => {
819
1042
  try {
820
1043
  const url = apiUrl || "https://api.forboc.ai";
1044
+ requireApiKeyGuidance(url, apiKey);
821
1045
  const data = await dispatch2(sdkApi.endpoints.getGhostHistory.initiate({ limit, apiUrl: url, apiKey })).unwrap();
822
1046
  return (data.sessions || []).map((s) => ({
823
1047
  sessionId: s.sessionId,
@@ -827,8 +1051,8 @@ var getGhostHistoryThunk = (0, import_toolkit3.createAsyncThunk)(
827
1051
  status: s.status,
828
1052
  passRate: s.passRate
829
1053
  }));
830
- } catch (e) {
831
- return rejectWithValue(e.message || "Failed to get ghost history");
1054
+ } catch (error) {
1055
+ return rejectWithValue(extractThunkErrorMessage(error, "Failed to get ghost history"));
832
1056
  }
833
1057
  }
834
1058
  );
@@ -863,7 +1087,7 @@ var ghostSlice = (0, import_toolkit3.createSlice)({
863
1087
  state.status = "completed";
864
1088
  }).addCase(stopGhostThunk.fulfilled, (state, action) => {
865
1089
  if (action.payload.stopped) {
866
- state.status = "failed";
1090
+ state.status = "completed";
867
1091
  } else {
868
1092
  state.error = action.payload.status || "Ghost stop request did not stop a session";
869
1093
  }
@@ -959,7 +1183,8 @@ var npcSlice = (0, import_toolkit4.createSlice)({
959
1183
  persona,
960
1184
  state: initialState5 || {},
961
1185
  history: [],
962
- isBlocked: false
1186
+ isBlocked: false,
1187
+ stateLog: [{ timestamp: Date.now(), delta: initialState5 || {}, state: initialState5 || {} }]
963
1188
  });
964
1189
  state.activeNpcId = id;
965
1190
  },
@@ -974,7 +1199,9 @@ var npcSlice = (0, import_toolkit4.createSlice)({
974
1199
  const { id, delta } = action.payload;
975
1200
  const npc = state.entities[id];
976
1201
  if (npc) {
977
- npc.state = { ...npc.state, ...delta };
1202
+ const newState = { ...npc.state, ...delta };
1203
+ npc.state = newState;
1204
+ npc.stateLog.push({ timestamp: Date.now(), delta, state: newState });
978
1205
  }
979
1206
  },
980
1207
  addToHistory: (state, action) => {
@@ -1044,6 +1271,7 @@ var initRemoteCortexThunk = (0, import_toolkit5.createAsyncThunk)(
1044
1271
  async ({ model = "api-integrated", authKey, apiUrl, apiKey }, { dispatch: dispatch2, rejectWithValue }) => {
1045
1272
  try {
1046
1273
  const url = apiUrl || "https://api.forboc.ai";
1274
+ requireApiKeyGuidance(url, apiKey);
1047
1275
  const data = await dispatch2(sdkApi.endpoints.postCortexInit.initiate({
1048
1276
  request: { requestedModel: model, authKey },
1049
1277
  apiUrl: url,
@@ -1055,8 +1283,8 @@ var initRemoteCortexThunk = (0, import_toolkit5.createAsyncThunk)(
1055
1283
  ready: data.state?.toLowerCase() === "ready",
1056
1284
  engine: "remote"
1057
1285
  };
1058
- } catch (e) {
1059
- return rejectWithValue(e.message || "Remote cortex init failed");
1286
+ } catch (error) {
1287
+ return rejectWithValue(extractThunkErrorMessage(error, "Remote cortex init failed"));
1060
1288
  }
1061
1289
  }
1062
1290
  );
@@ -1065,9 +1293,10 @@ var listCortexModelsThunk = (0, import_toolkit5.createAsyncThunk)(
1065
1293
  async ({ apiUrl, apiKey }, { dispatch: dispatch2, rejectWithValue }) => {
1066
1294
  try {
1067
1295
  const url = apiUrl || "https://api.forboc.ai";
1296
+ requireApiKeyGuidance(url, apiKey);
1068
1297
  return await dispatch2(sdkApi.endpoints.getCortexModels.initiate({ apiUrl: url, apiKey })).unwrap();
1069
- } catch (e) {
1070
- return rejectWithValue(e.message || "Failed to list cortex models");
1298
+ } catch (error) {
1299
+ return rejectWithValue(extractThunkErrorMessage(error, "Failed to list cortex models"));
1071
1300
  }
1072
1301
  }
1073
1302
  );
@@ -1075,6 +1304,7 @@ var completeRemoteThunk = (0, import_toolkit5.createAsyncThunk)(
1075
1304
  "cortex/completeRemote",
1076
1305
  async ({ cortexId, prompt, options, apiUrl, apiKey }, { dispatch: dispatch2, rejectWithValue }) => {
1077
1306
  try {
1307
+ requireApiKeyGuidance(apiUrl, apiKey);
1078
1308
  const data = await dispatch2(sdkApi.endpoints.postCortexComplete.initiate({
1079
1309
  cortexId,
1080
1310
  prompt,
@@ -1083,8 +1313,8 @@ var completeRemoteThunk = (0, import_toolkit5.createAsyncThunk)(
1083
1313
  apiKey
1084
1314
  })).unwrap();
1085
1315
  return data.text;
1086
- } catch (e) {
1087
- return rejectWithValue(e.message || "Remote completing failed");
1316
+ } catch (error) {
1317
+ return rejectWithValue(extractThunkErrorMessage(error, "Remote completing failed"));
1088
1318
  }
1089
1319
  }
1090
1320
  );
@@ -1319,7 +1549,7 @@ var dispatch = store.dispatch;
1319
1549
 
1320
1550
  // src/thunks.ts
1321
1551
  var import_toolkit10 = require("@reduxjs/toolkit");
1322
- var extractThunkErrorMessage = (e) => {
1552
+ var extractThunkErrorMessage2 = (e) => {
1323
1553
  if (typeof e === "string") return e;
1324
1554
  if (e?.data?.message) return String(e.data.message);
1325
1555
  if (e?.error) return String(e.error);
@@ -1453,7 +1683,7 @@ var processNPC = (0, import_toolkit10.createAsyncThunk)(
1453
1683
  };
1454
1684
  return runProtocolRecursively(initialTape, void 0, 0);
1455
1685
  } catch (e) {
1456
- const message = extractThunkErrorMessage(e);
1686
+ const message = extractThunkErrorMessage2(e);
1457
1687
  dispatch2(directiveRunFailed({ id: directiveId, error: String(message) }));
1458
1688
  return rejectWithValue(String(message));
1459
1689
  }
@@ -1574,6 +1804,8 @@ var clearMemoryRemoteThunk = (0, import_toolkit10.createAsyncThunk)(
1574
1804
  getGhostStatusThunk,
1575
1805
  getSoulListThunk,
1576
1806
  ghostSlice,
1807
+ handler_ArweaveDownload,
1808
+ handler_ArweaveUpload,
1577
1809
  importNpcFromSoulThunk,
1578
1810
  importSoulFromArweaveThunk,
1579
1811
  initRemoteCortexThunk,