@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.cjs CHANGED
@@ -21,6 +21,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var loader_exports = {};
22
22
  __export(loader_exports, {
23
23
  computeContentHash: () => computeContentHash,
24
+ computeContentHashV2: () => computeContentHashV2,
24
25
  detectFormat: () => detectFormat,
25
26
  getContainerFormat: () => getContainerFormat,
26
27
  mightBeCard: () => mightBeCard,
@@ -492,7 +493,7 @@ var CCv2LorebookEntrySchema = import_zod2.z.object({
492
493
  content: import_zod2.z.string(),
493
494
  enabled: import_zod2.z.boolean().default(true),
494
495
  // Default to enabled if missing
495
- insertion_order: import_zod2.z.number().int().nullable().default(0),
496
+ insertion_order: import_zod2.z.preprocess((v) => v ?? 0, import_zod2.z.number().int()),
496
497
  // Optional fields - be lenient with nulls since wild data has them
497
498
  extensions: import_zod2.z.record(import_zod2.z.unknown()).optional(),
498
499
  case_sensitive: import_zod2.z.boolean().nullable().optional(),
@@ -503,7 +504,7 @@ var CCv2LorebookEntrySchema = import_zod2.z.object({
503
504
  selective: import_zod2.z.boolean().nullable().optional(),
504
505
  secondary_keys: import_zod2.z.array(import_zod2.z.string()).nullable().optional(),
505
506
  constant: import_zod2.z.boolean().nullable().optional(),
506
- position: import_zod2.z.union([import_zod2.z.enum(["before_char", "after_char"]), import_zod2.z.number().int(), import_zod2.z.literal("")]).nullable().optional()
507
+ position: import_zod2.z.union([import_zod2.z.enum(["before_char", "after_char", "in_chat"]), import_zod2.z.number().int(), import_zod2.z.literal("")]).nullable().optional()
507
508
  }).passthrough();
508
509
  var CCv2CharacterBookSchema = import_zod2.z.object({
509
510
  name: import_zod2.z.string().optional(),
@@ -557,7 +558,7 @@ var CCv3LorebookEntrySchema = import_zod3.z.object({
557
558
  content: import_zod3.z.string(),
558
559
  enabled: import_zod3.z.boolean().default(true),
559
560
  // Default to enabled if missing
560
- insertion_order: import_zod3.z.number().int().nullable().default(0),
561
+ insertion_order: import_zod3.z.preprocess((v) => v ?? 0, import_zod3.z.number().int()),
561
562
  // Optional fields - be lenient with nulls since wild data has them
562
563
  case_sensitive: import_zod3.z.boolean().nullable().optional(),
563
564
  name: import_zod3.z.string().optional(),
@@ -567,7 +568,7 @@ var CCv3LorebookEntrySchema = import_zod3.z.object({
567
568
  selective: import_zod3.z.boolean().nullable().optional(),
568
569
  secondary_keys: import_zod3.z.array(import_zod3.z.string()).nullable().optional(),
569
570
  constant: import_zod3.z.boolean().nullable().optional(),
570
- position: import_zod3.z.union([import_zod3.z.enum(["before_char", "after_char"]), import_zod3.z.number().int(), import_zod3.z.literal("")]).nullable().optional(),
571
+ position: import_zod3.z.union([import_zod3.z.enum(["before_char", "after_char", "in_chat"]), import_zod3.z.number().int(), import_zod3.z.literal("")]).nullable().optional(),
571
572
  extensions: import_zod3.z.record(import_zod3.z.unknown()).optional(),
572
573
  // v3 specific - also lenient with types since SillyTavern uses numbers for enums
573
574
  automation_id: import_zod3.z.string().optional(),
@@ -8727,7 +8728,7 @@ function defaultTokenCounter(_card) {
8727
8728
  total: 0
8728
8729
  };
8729
8730
  }
8730
- function getCanonicalContent(card) {
8731
+ function getCanonicalContentV1(card) {
8731
8732
  const normalized = {
8732
8733
  name: card.data.name,
8733
8734
  description: card.data.description || "",
@@ -8749,6 +8750,69 @@ function getCanonicalContent(card) {
8749
8750
  };
8750
8751
  return JSON.stringify(normalized, Object.keys(normalized).sort());
8751
8752
  }
8753
+ function stableStringify(value) {
8754
+ if (value === null) return "null";
8755
+ switch (typeof value) {
8756
+ case "string":
8757
+ return JSON.stringify(value);
8758
+ case "number":
8759
+ return Number.isFinite(value) ? String(value) : "null";
8760
+ case "boolean":
8761
+ return value ? "true" : "false";
8762
+ case "bigint":
8763
+ return JSON.stringify(value.toString());
8764
+ case "undefined":
8765
+ case "function":
8766
+ case "symbol":
8767
+ return "null";
8768
+ case "object": {
8769
+ if (Array.isArray(value)) {
8770
+ const parts2 = value.map((item) => {
8771
+ if (item === void 0 || typeof item === "function" || typeof item === "symbol") {
8772
+ return "null";
8773
+ }
8774
+ return stableStringify(item);
8775
+ });
8776
+ return `[${parts2.join(",")}]`;
8777
+ }
8778
+ const obj = value;
8779
+ const keys = Object.keys(obj).sort();
8780
+ const parts = [];
8781
+ for (const key of keys) {
8782
+ const v = obj[key];
8783
+ if (v === void 0 || typeof v === "function" || typeof v === "symbol") {
8784
+ continue;
8785
+ }
8786
+ parts.push(`${JSON.stringify(key)}:${stableStringify(v)}`);
8787
+ }
8788
+ return `{${parts.join(",")}}`;
8789
+ }
8790
+ default:
8791
+ return "null";
8792
+ }
8793
+ }
8794
+ function getCanonicalContentV2(card) {
8795
+ const normalized = {
8796
+ name: card.data.name,
8797
+ description: card.data.description || "",
8798
+ personality: card.data.personality || "",
8799
+ scenario: card.data.scenario || "",
8800
+ first_mes: card.data.first_mes || "",
8801
+ mes_example: card.data.mes_example || "",
8802
+ system_prompt: card.data.system_prompt || "",
8803
+ post_history_instructions: card.data.post_history_instructions || "",
8804
+ alternate_greetings: card.data.alternate_greetings || [],
8805
+ character_book: card.data.character_book ? {
8806
+ entries: (card.data.character_book.entries || []).map((e) => ({
8807
+ keys: e.keys,
8808
+ content: e.content,
8809
+ enabled: e.enabled
8810
+ }))
8811
+ } : null,
8812
+ creator_notes: card.data.creator_notes || ""
8813
+ };
8814
+ return stableStringify(normalized);
8815
+ }
8752
8816
  function isWithinTolerance(clientValue, computedValue, tolerance) {
8753
8817
  if (clientValue === void 0) return false;
8754
8818
  if (computedValue === 0) return clientValue === 0;
@@ -8769,15 +8833,18 @@ async function validateClientMetadata(clientMetadata, parseResult, options = {})
8769
8833
  const warnings = [];
8770
8834
  const errors = [];
8771
8835
  const computedTokens = countTokens(card);
8772
- const canonicalContent = getCanonicalContent(card);
8773
- const computedHash = await computeHash(canonicalContent);
8836
+ const canonicalContentV1 = getCanonicalContentV1(card);
8837
+ const canonicalContentV2 = getCanonicalContentV2(card);
8838
+ const computedHashV1 = await computeHash(canonicalContentV1);
8839
+ const computedHashV2 = await computeHash(canonicalContentV2);
8774
8840
  const entries = card.data.character_book?.entries || [];
8775
8841
  const computedHasLorebook = entries.length > 0;
8776
8842
  const computedLorebookCount = entries.length;
8777
8843
  const authoritative = {
8778
8844
  name: card.data.name,
8779
8845
  tokens: computedTokens,
8780
- contentHash: computedHash,
8846
+ contentHash: computedHashV1,
8847
+ contentHashV2: computedHashV2,
8781
8848
  hasLorebook: computedHasLorebook,
8782
8849
  lorebookEntriesCount: computedLorebookCount
8783
8850
  };
@@ -8789,21 +8856,27 @@ async function validateClientMetadata(clientMetadata, parseResult, options = {})
8789
8856
  withinTolerance: false
8790
8857
  });
8791
8858
  }
8792
- if (clientMetadata.contentHash !== computedHash) {
8859
+ const matchesV1 = clientMetadata.contentHash === computedHashV1;
8860
+ const matchesV2 = clientMetadata.contentHash === computedHashV2;
8861
+ if (!matchesV1 && !matchesV2) {
8793
8862
  const disc = {
8794
8863
  field: "contentHash",
8795
8864
  clientValue: clientMetadata.contentHash,
8796
- computedValue: computedHash,
8865
+ computedValue: computedHashV1,
8797
8866
  withinTolerance: false
8798
8867
  };
8799
8868
  discrepancies.push(disc);
8800
8869
  if (allowHashMismatch) {
8801
8870
  warnings.push(
8802
- `Content hash mismatch: client=${clientMetadata.contentHash.substring(0, 8)}..., server=${computedHash.substring(0, 8)}...`
8871
+ `Content hash mismatch: client=${clientMetadata.contentHash.substring(0, 8)}..., server(v1)=${computedHashV1.substring(0, 8)}..., server(v2)=${computedHashV2.substring(0, 8)}...`
8803
8872
  );
8804
8873
  } else {
8805
8874
  errors.push("Content hash mismatch - possible tampering or encoding difference");
8806
8875
  }
8876
+ } else if (matchesV1 && !matchesV2) {
8877
+ warnings.push(
8878
+ "Client contentHash matches legacy v1 canonicalization. Prefer authoritative.contentHashV2 for new storage."
8879
+ );
8807
8880
  }
8808
8881
  const tokenFields = [
8809
8882
  "description",
@@ -8884,7 +8957,11 @@ async function validateClientMetadata(clientMetadata, parseResult, options = {})
8884
8957
  };
8885
8958
  }
8886
8959
  async function computeContentHash(card) {
8887
- const content = getCanonicalContent(card);
8960
+ const content = getCanonicalContentV1(card);
8961
+ return sha256Hash(content);
8962
+ }
8963
+ async function computeContentHashV2(card) {
8964
+ const content = getCanonicalContentV2(card);
8888
8965
  return sha256Hash(content);
8889
8966
  }
8890
8967
  function validateClientMetadataSync(clientMetadata, parseResult, options) {
@@ -8900,15 +8977,18 @@ function validateClientMetadataSync(clientMetadata, parseResult, options) {
8900
8977
  const warnings = [];
8901
8978
  const errors = [];
8902
8979
  const computedTokens = countTokens(card);
8903
- const canonicalContent = getCanonicalContent(card);
8904
- const computedHash = computeHash(canonicalContent);
8980
+ const canonicalContentV1 = getCanonicalContentV1(card);
8981
+ const canonicalContentV2 = getCanonicalContentV2(card);
8982
+ const computedHashV1 = computeHash(canonicalContentV1);
8983
+ const computedHashV2 = computeHash(canonicalContentV2);
8905
8984
  const entries = card.data.character_book?.entries || [];
8906
8985
  const computedHasLorebook = entries.length > 0;
8907
8986
  const computedLorebookCount = entries.length;
8908
8987
  const authoritative = {
8909
8988
  name: card.data.name,
8910
8989
  tokens: computedTokens,
8911
- contentHash: computedHash,
8990
+ contentHash: computedHashV1,
8991
+ contentHashV2: computedHashV2,
8912
8992
  hasLorebook: computedHasLorebook,
8913
8993
  lorebookEntriesCount: computedLorebookCount
8914
8994
  };
@@ -8920,11 +9000,13 @@ function validateClientMetadataSync(clientMetadata, parseResult, options) {
8920
9000
  withinTolerance: false
8921
9001
  });
8922
9002
  }
8923
- if (clientMetadata.contentHash !== computedHash) {
9003
+ const matchesV1 = clientMetadata.contentHash === computedHashV1;
9004
+ const matchesV2 = clientMetadata.contentHash === computedHashV2;
9005
+ if (!matchesV1 && !matchesV2) {
8924
9006
  discrepancies.push({
8925
9007
  field: "contentHash",
8926
9008
  clientValue: clientMetadata.contentHash,
8927
- computedValue: computedHash,
9009
+ computedValue: computedHashV1,
8928
9010
  withinTolerance: false
8929
9011
  });
8930
9012
  if (allowHashMismatch) {
@@ -8932,6 +9014,10 @@ function validateClientMetadataSync(clientMetadata, parseResult, options) {
8932
9014
  } else {
8933
9015
  errors.push("Content hash mismatch");
8934
9016
  }
9017
+ } else if (matchesV1 && !matchesV2) {
9018
+ warnings.push(
9019
+ "Client contentHash matches legacy v1 canonicalization. Prefer authoritative.contentHashV2 for new storage."
9020
+ );
8935
9021
  }
8936
9022
  const tokenFields = [
8937
9023
  "description",