@character-foundry/character-foundry 0.4.1 → 0.4.2-dev.1765997746

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.
Files changed (57) hide show
  1. package/dist/charx.cjs +17 -38
  2. package/dist/charx.cjs.map +1 -1
  3. package/dist/charx.d.cts +27 -18
  4. package/dist/charx.d.ts +27 -18
  5. package/dist/charx.js +17 -38
  6. package/dist/charx.js.map +1 -1
  7. package/dist/exporter.cjs +36 -40
  8. package/dist/exporter.cjs.map +1 -1
  9. package/dist/exporter.d.cts +27 -18
  10. package/dist/exporter.d.ts +27 -18
  11. package/dist/exporter.js +36 -40
  12. package/dist/exporter.js.map +1 -1
  13. package/dist/federation.cjs +104 -36
  14. package/dist/federation.cjs.map +1 -1
  15. package/dist/federation.d.cts +62 -18
  16. package/dist/federation.d.ts +62 -18
  17. package/dist/federation.js +104 -36
  18. package/dist/federation.js.map +1 -1
  19. package/dist/index.cjs +36 -40
  20. package/dist/index.cjs.map +1 -1
  21. package/dist/index.d.cts +63 -42
  22. package/dist/index.d.ts +63 -42
  23. package/dist/index.js +36 -40
  24. package/dist/index.js.map +1 -1
  25. package/dist/loader.cjs +103 -17
  26. package/dist/loader.cjs.map +1 -1
  27. package/dist/loader.d.cts +56 -28
  28. package/dist/loader.d.ts +56 -28
  29. package/dist/loader.js +103 -17
  30. package/dist/loader.js.map +1 -1
  31. package/dist/lorebook.d.cts +51 -34
  32. package/dist/lorebook.d.ts +51 -34
  33. package/dist/normalizer.cjs +4 -4
  34. package/dist/normalizer.cjs.map +1 -1
  35. package/dist/normalizer.d.cts +90 -60
  36. package/dist/normalizer.d.ts +90 -60
  37. package/dist/normalizer.js +4 -4
  38. package/dist/normalizer.js.map +1 -1
  39. package/dist/png.cjs +4 -4
  40. package/dist/png.cjs.map +1 -1
  41. package/dist/png.d.cts +48 -32
  42. package/dist/png.d.ts +48 -32
  43. package/dist/png.js +4 -4
  44. package/dist/png.js.map +1 -1
  45. package/dist/schemas.cjs +9 -9
  46. package/dist/schemas.cjs.map +1 -1
  47. package/dist/schemas.d.cts +144 -96
  48. package/dist/schemas.d.ts +144 -96
  49. package/dist/schemas.js +9 -9
  50. package/dist/schemas.js.map +1 -1
  51. package/dist/voxta.cjs +23 -6
  52. package/dist/voxta.cjs.map +1 -1
  53. package/dist/voxta.d.cts +42 -28
  54. package/dist/voxta.d.ts +42 -28
  55. package/dist/voxta.js +23 -6
  56. package/dist/voxta.js.map +1 -1
  57. package/package.json +6 -6
package/dist/loader.js CHANGED
@@ -462,7 +462,7 @@ var CCv2LorebookEntrySchema = z2.object({
462
462
  content: z2.string(),
463
463
  enabled: z2.boolean().default(true),
464
464
  // Default to enabled if missing
465
- insertion_order: z2.number().int().nullable().default(0),
465
+ insertion_order: z2.preprocess((v) => v ?? 0, z2.number().int()),
466
466
  // Optional fields - be lenient with nulls since wild data has them
467
467
  extensions: z2.record(z2.unknown()).optional(),
468
468
  case_sensitive: z2.boolean().nullable().optional(),
@@ -473,7 +473,7 @@ var CCv2LorebookEntrySchema = z2.object({
473
473
  selective: z2.boolean().nullable().optional(),
474
474
  secondary_keys: z2.array(z2.string()).nullable().optional(),
475
475
  constant: z2.boolean().nullable().optional(),
476
- position: z2.union([z2.enum(["before_char", "after_char"]), z2.number().int(), z2.literal("")]).nullable().optional()
476
+ position: z2.union([z2.enum(["before_char", "after_char", "in_chat"]), z2.number().int(), z2.literal("")]).nullable().optional()
477
477
  }).passthrough();
478
478
  var CCv2CharacterBookSchema = z2.object({
479
479
  name: z2.string().optional(),
@@ -527,7 +527,7 @@ var CCv3LorebookEntrySchema = z3.object({
527
527
  content: z3.string(),
528
528
  enabled: z3.boolean().default(true),
529
529
  // Default to enabled if missing
530
- insertion_order: z3.number().int().nullable().default(0),
530
+ insertion_order: z3.preprocess((v) => v ?? 0, z3.number().int()),
531
531
  // Optional fields - be lenient with nulls since wild data has them
532
532
  case_sensitive: z3.boolean().nullable().optional(),
533
533
  name: z3.string().optional(),
@@ -537,7 +537,7 @@ var CCv3LorebookEntrySchema = z3.object({
537
537
  selective: z3.boolean().nullable().optional(),
538
538
  secondary_keys: z3.array(z3.string()).nullable().optional(),
539
539
  constant: z3.boolean().nullable().optional(),
540
- position: z3.union([z3.enum(["before_char", "after_char"]), z3.number().int(), z3.literal("")]).nullable().optional(),
540
+ position: z3.union([z3.enum(["before_char", "after_char", "in_chat"]), z3.number().int(), z3.literal("")]).nullable().optional(),
541
541
  extensions: z3.record(z3.unknown()).optional(),
542
542
  // v3 specific - also lenient with types since SillyTavern uses numbers for enums
543
543
  automation_id: z3.string().optional(),
@@ -8697,7 +8697,7 @@ function defaultTokenCounter(_card) {
8697
8697
  total: 0
8698
8698
  };
8699
8699
  }
8700
- function getCanonicalContent(card) {
8700
+ function getCanonicalContentV1(card) {
8701
8701
  const normalized = {
8702
8702
  name: card.data.name,
8703
8703
  description: card.data.description || "",
@@ -8719,6 +8719,69 @@ function getCanonicalContent(card) {
8719
8719
  };
8720
8720
  return JSON.stringify(normalized, Object.keys(normalized).sort());
8721
8721
  }
8722
+ function stableStringify(value) {
8723
+ if (value === null) return "null";
8724
+ switch (typeof value) {
8725
+ case "string":
8726
+ return JSON.stringify(value);
8727
+ case "number":
8728
+ return Number.isFinite(value) ? String(value) : "null";
8729
+ case "boolean":
8730
+ return value ? "true" : "false";
8731
+ case "bigint":
8732
+ return JSON.stringify(value.toString());
8733
+ case "undefined":
8734
+ case "function":
8735
+ case "symbol":
8736
+ return "null";
8737
+ case "object": {
8738
+ if (Array.isArray(value)) {
8739
+ const parts2 = value.map((item) => {
8740
+ if (item === void 0 || typeof item === "function" || typeof item === "symbol") {
8741
+ return "null";
8742
+ }
8743
+ return stableStringify(item);
8744
+ });
8745
+ return `[${parts2.join(",")}]`;
8746
+ }
8747
+ const obj = value;
8748
+ const keys = Object.keys(obj).sort();
8749
+ const parts = [];
8750
+ for (const key of keys) {
8751
+ const v = obj[key];
8752
+ if (v === void 0 || typeof v === "function" || typeof v === "symbol") {
8753
+ continue;
8754
+ }
8755
+ parts.push(`${JSON.stringify(key)}:${stableStringify(v)}`);
8756
+ }
8757
+ return `{${parts.join(",")}}`;
8758
+ }
8759
+ default:
8760
+ return "null";
8761
+ }
8762
+ }
8763
+ function getCanonicalContentV2(card) {
8764
+ const normalized = {
8765
+ name: card.data.name,
8766
+ description: card.data.description || "",
8767
+ personality: card.data.personality || "",
8768
+ scenario: card.data.scenario || "",
8769
+ first_mes: card.data.first_mes || "",
8770
+ mes_example: card.data.mes_example || "",
8771
+ system_prompt: card.data.system_prompt || "",
8772
+ post_history_instructions: card.data.post_history_instructions || "",
8773
+ alternate_greetings: card.data.alternate_greetings || [],
8774
+ character_book: card.data.character_book ? {
8775
+ entries: (card.data.character_book.entries || []).map((e) => ({
8776
+ keys: e.keys,
8777
+ content: e.content,
8778
+ enabled: e.enabled
8779
+ }))
8780
+ } : null,
8781
+ creator_notes: card.data.creator_notes || ""
8782
+ };
8783
+ return stableStringify(normalized);
8784
+ }
8722
8785
  function isWithinTolerance(clientValue, computedValue, tolerance) {
8723
8786
  if (clientValue === void 0) return false;
8724
8787
  if (computedValue === 0) return clientValue === 0;
@@ -8739,15 +8802,18 @@ async function validateClientMetadata(clientMetadata, parseResult, options = {})
8739
8802
  const warnings = [];
8740
8803
  const errors = [];
8741
8804
  const computedTokens = countTokens(card);
8742
- const canonicalContent = getCanonicalContent(card);
8743
- const computedHash = await computeHash(canonicalContent);
8805
+ const canonicalContentV1 = getCanonicalContentV1(card);
8806
+ const canonicalContentV2 = getCanonicalContentV2(card);
8807
+ const computedHashV1 = await computeHash(canonicalContentV1);
8808
+ const computedHashV2 = await computeHash(canonicalContentV2);
8744
8809
  const entries = card.data.character_book?.entries || [];
8745
8810
  const computedHasLorebook = entries.length > 0;
8746
8811
  const computedLorebookCount = entries.length;
8747
8812
  const authoritative = {
8748
8813
  name: card.data.name,
8749
8814
  tokens: computedTokens,
8750
- contentHash: computedHash,
8815
+ contentHash: computedHashV1,
8816
+ contentHashV2: computedHashV2,
8751
8817
  hasLorebook: computedHasLorebook,
8752
8818
  lorebookEntriesCount: computedLorebookCount
8753
8819
  };
@@ -8759,21 +8825,27 @@ async function validateClientMetadata(clientMetadata, parseResult, options = {})
8759
8825
  withinTolerance: false
8760
8826
  });
8761
8827
  }
8762
- if (clientMetadata.contentHash !== computedHash) {
8828
+ const matchesV1 = clientMetadata.contentHash === computedHashV1;
8829
+ const matchesV2 = clientMetadata.contentHash === computedHashV2;
8830
+ if (!matchesV1 && !matchesV2) {
8763
8831
  const disc = {
8764
8832
  field: "contentHash",
8765
8833
  clientValue: clientMetadata.contentHash,
8766
- computedValue: computedHash,
8834
+ computedValue: computedHashV1,
8767
8835
  withinTolerance: false
8768
8836
  };
8769
8837
  discrepancies.push(disc);
8770
8838
  if (allowHashMismatch) {
8771
8839
  warnings.push(
8772
- `Content hash mismatch: client=${clientMetadata.contentHash.substring(0, 8)}..., server=${computedHash.substring(0, 8)}...`
8840
+ `Content hash mismatch: client=${clientMetadata.contentHash.substring(0, 8)}..., server(v1)=${computedHashV1.substring(0, 8)}..., server(v2)=${computedHashV2.substring(0, 8)}...`
8773
8841
  );
8774
8842
  } else {
8775
8843
  errors.push("Content hash mismatch - possible tampering or encoding difference");
8776
8844
  }
8845
+ } else if (matchesV1 && !matchesV2) {
8846
+ warnings.push(
8847
+ "Client contentHash matches legacy v1 canonicalization. Prefer authoritative.contentHashV2 for new storage."
8848
+ );
8777
8849
  }
8778
8850
  const tokenFields = [
8779
8851
  "description",
@@ -8854,7 +8926,11 @@ async function validateClientMetadata(clientMetadata, parseResult, options = {})
8854
8926
  };
8855
8927
  }
8856
8928
  async function computeContentHash(card) {
8857
- const content = getCanonicalContent(card);
8929
+ const content = getCanonicalContentV1(card);
8930
+ return sha256Hash(content);
8931
+ }
8932
+ async function computeContentHashV2(card) {
8933
+ const content = getCanonicalContentV2(card);
8858
8934
  return sha256Hash(content);
8859
8935
  }
8860
8936
  function validateClientMetadataSync(clientMetadata, parseResult, options) {
@@ -8870,15 +8946,18 @@ function validateClientMetadataSync(clientMetadata, parseResult, options) {
8870
8946
  const warnings = [];
8871
8947
  const errors = [];
8872
8948
  const computedTokens = countTokens(card);
8873
- const canonicalContent = getCanonicalContent(card);
8874
- const computedHash = computeHash(canonicalContent);
8949
+ const canonicalContentV1 = getCanonicalContentV1(card);
8950
+ const canonicalContentV2 = getCanonicalContentV2(card);
8951
+ const computedHashV1 = computeHash(canonicalContentV1);
8952
+ const computedHashV2 = computeHash(canonicalContentV2);
8875
8953
  const entries = card.data.character_book?.entries || [];
8876
8954
  const computedHasLorebook = entries.length > 0;
8877
8955
  const computedLorebookCount = entries.length;
8878
8956
  const authoritative = {
8879
8957
  name: card.data.name,
8880
8958
  tokens: computedTokens,
8881
- contentHash: computedHash,
8959
+ contentHash: computedHashV1,
8960
+ contentHashV2: computedHashV2,
8882
8961
  hasLorebook: computedHasLorebook,
8883
8962
  lorebookEntriesCount: computedLorebookCount
8884
8963
  };
@@ -8890,11 +8969,13 @@ function validateClientMetadataSync(clientMetadata, parseResult, options) {
8890
8969
  withinTolerance: false
8891
8970
  });
8892
8971
  }
8893
- if (clientMetadata.contentHash !== computedHash) {
8972
+ const matchesV1 = clientMetadata.contentHash === computedHashV1;
8973
+ const matchesV2 = clientMetadata.contentHash === computedHashV2;
8974
+ if (!matchesV1 && !matchesV2) {
8894
8975
  discrepancies.push({
8895
8976
  field: "contentHash",
8896
8977
  clientValue: clientMetadata.contentHash,
8897
- computedValue: computedHash,
8978
+ computedValue: computedHashV1,
8898
8979
  withinTolerance: false
8899
8980
  });
8900
8981
  if (allowHashMismatch) {
@@ -8902,6 +8983,10 @@ function validateClientMetadataSync(clientMetadata, parseResult, options) {
8902
8983
  } else {
8903
8984
  errors.push("Content hash mismatch");
8904
8985
  }
8986
+ } else if (matchesV1 && !matchesV2) {
8987
+ warnings.push(
8988
+ "Client contentHash matches legacy v1 canonicalization. Prefer authoritative.contentHashV2 for new storage."
8989
+ );
8905
8990
  }
8906
8991
  const tokenFields = [
8907
8992
  "description",
@@ -8970,6 +9055,7 @@ function validateClientMetadataSync(clientMetadata, parseResult, options) {
8970
9055
  }
8971
9056
  export {
8972
9057
  computeContentHash,
9058
+ computeContentHashV2,
8973
9059
  detectFormat,
8974
9060
  getContainerFormat,
8975
9061
  mightBeCard,