@lucern/graph-primitives 1.0.16 → 1.0.18

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 (92) hide show
  1. package/dist/beliefEvidenceLinks.js +144 -99
  2. package/dist/beliefEvidenceLinks.js.map +1 -1
  3. package/dist/beliefEvidenceLinks.operational.d.ts +29 -0
  4. package/dist/beliefEvidenceLinks.operational.js +157 -0
  5. package/dist/beliefEvidenceLinks.operational.js.map +1 -0
  6. package/dist/{beliefLifecycle-y8WLXqQj.d.ts → beliefLifecycle-CXwdDw5e.d.ts} +7 -4
  7. package/dist/beliefLifecycle.d.ts +1 -1
  8. package/dist/beliefLifecycle.js +75 -18
  9. package/dist/beliefLifecycle.js.map +1 -1
  10. package/dist/entityLifecycle.js +1 -12
  11. package/dist/entityLifecycle.js.map +1 -1
  12. package/dist/epistemicAnswers.js +1 -12
  13. package/dist/epistemicAnswers.js.map +1 -1
  14. package/dist/epistemicBeliefs.admin.js.map +1 -1
  15. package/dist/epistemicBeliefs.backfills.d.ts +1 -1
  16. package/dist/epistemicBeliefs.backfills.js +63 -35
  17. package/dist/epistemicBeliefs.backfills.js.map +1 -1
  18. package/dist/epistemicBeliefs.confidence.d.ts +1 -1
  19. package/dist/epistemicBeliefs.confidence.js +70 -41
  20. package/dist/epistemicBeliefs.confidence.js.map +1 -1
  21. package/dist/epistemicBeliefs.core.js +946 -566
  22. package/dist/epistemicBeliefs.core.js.map +1 -1
  23. package/dist/epistemicBeliefs.d.ts +2 -2
  24. package/dist/epistemicBeliefs.forkEvidence.d.ts +18 -0
  25. package/dist/epistemicBeliefs.forkEvidence.js +121 -0
  26. package/dist/epistemicBeliefs.forkEvidence.js.map +1 -0
  27. package/dist/epistemicBeliefs.helpers.d.ts +2 -2
  28. package/dist/epistemicBeliefs.helpers.js +60 -32
  29. package/dist/epistemicBeliefs.helpers.js.map +1 -1
  30. package/dist/epistemicBeliefs.internal.js +175 -51
  31. package/dist/epistemicBeliefs.internal.js.map +1 -1
  32. package/dist/epistemicBeliefs.js +437 -84
  33. package/dist/epistemicBeliefs.js.map +1 -1
  34. package/dist/epistemicBeliefs.lifecycle.d.ts +2 -2
  35. package/dist/epistemicBeliefs.lifecycle.js +75 -47
  36. package/dist/epistemicBeliefs.lifecycle.js.map +1 -1
  37. package/dist/epistemicBeliefs.links.js +47 -13
  38. package/dist/epistemicBeliefs.links.js.map +1 -1
  39. package/dist/epistemicBeliefs.topicAnchor.d.ts +29 -0
  40. package/dist/epistemicBeliefs.topicAnchor.js +105 -0
  41. package/dist/epistemicBeliefs.topicAnchor.js.map +1 -0
  42. package/dist/epistemicContracts.evaluators.js +71 -42
  43. package/dist/epistemicContracts.evaluators.js.map +1 -1
  44. package/dist/epistemicContracts.handlers.js +72 -54
  45. package/dist/epistemicContracts.handlers.js.map +1 -1
  46. package/dist/epistemicContracts.js +72 -54
  47. package/dist/epistemicContracts.js.map +1 -1
  48. package/dist/epistemicContracts.metrics.js +1 -1
  49. package/dist/epistemicContracts.metrics.js.map +1 -1
  50. package/dist/epistemicContracts.types.d.ts +1 -1
  51. package/dist/epistemicEdgeCreation.js +1 -12
  52. package/dist/epistemicEdgeCreation.js.map +1 -1
  53. package/dist/epistemicEdges.helpers.d.ts +1 -1
  54. package/dist/epistemicEvidence.js +173 -93
  55. package/dist/epistemicEvidence.js.map +1 -1
  56. package/dist/epistemicEvidenceMutations.js +173 -93
  57. package/dist/epistemicEvidenceMutations.js.map +1 -1
  58. package/dist/epistemicHelpers.js +1 -12
  59. package/dist/epistemicHelpers.js.map +1 -1
  60. package/dist/epistemicNodeCreation.js +1 -10
  61. package/dist/epistemicNodeCreation.js.map +1 -1
  62. package/dist/epistemicNodes.internal.js.map +1 -1
  63. package/dist/epistemicNodes.js +2 -2
  64. package/dist/epistemicNodes.js.map +1 -1
  65. package/dist/epistemicNodes.mutations.js +2 -2
  66. package/dist/epistemicNodes.mutations.js.map +1 -1
  67. package/dist/epistemicQuestions.create.js +1 -12
  68. package/dist/epistemicQuestions.create.js.map +1 -1
  69. package/dist/epistemicQuestions.evidence.js +1 -12
  70. package/dist/epistemicQuestions.evidence.js.map +1 -1
  71. package/dist/epistemicQuestions.js +1 -12
  72. package/dist/epistemicQuestions.js.map +1 -1
  73. package/dist/epistemicQuestions.tail.js +1 -12
  74. package/dist/epistemicQuestions.tail.js.map +1 -1
  75. package/dist/epistemicSources.js +1 -12
  76. package/dist/epistemicSources.js.map +1 -1
  77. package/dist/evaluators/index.js +1 -1
  78. package/dist/evaluators/index.js.map +1 -1
  79. package/dist/globalId-4y9SPpC_.d.ts +10 -0
  80. package/dist/globalId.d.ts +1 -1
  81. package/dist/globalId.js +1 -13
  82. package/dist/globalId.js.map +1 -1
  83. package/dist/helpers.js +1 -12
  84. package/dist/helpers.js.map +1 -1
  85. package/dist/index.d.ts +4 -3
  86. package/dist/index.js +771 -247
  87. package/dist/index.js.map +1 -1
  88. package/dist/invariantEnforcement.js +2 -2
  89. package/dist/invariantEnforcement.js.map +1 -1
  90. package/dist/proof-attestation.json +3 -3
  91. package/package.json +4 -4
  92. package/dist/globalId-DKh9d_uD.d.ts +0 -20
@@ -2,6 +2,7 @@ import { v } from 'convex/values';
2
2
  import { requireProjectAccess, checkProjectAccess } from '@lucern/access-control/access';
3
3
  import { permissiveReturn } from '@lucern/contracts/schema-helpers/validators';
4
4
  import { componentsGeneric, anyApi, mutationGeneric, internalMutationGeneric } from 'convex/server';
5
+ import { generateGlobalId } from '@lucern/contracts/ids';
5
6
  import { isNodeType, getLayerForNodeType } from '@lucern/contracts/schema-helpers/spine/tables/epistemicNodes';
6
7
  import '@lucern/access-control/audience';
7
8
 
@@ -52,18 +53,6 @@ async function scheduleEmbeddingGeneration(args) {
52
53
  );
53
54
  }
54
55
  }
55
-
56
- // src/globalId.ts
57
- function generateGlobalId() {
58
- const bytes = new Uint8Array(16);
59
- crypto.getRandomValues(bytes);
60
- bytes[6] = bytes[6] & 15 | 64;
61
- bytes[8] = bytes[8] & 63 | 128;
62
- const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join(
63
- ""
64
- );
65
- return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
66
- }
67
56
  var LEGACY_SCOPE_FIELD = "graphScopeProjectId";
68
57
  function asMappedProjectId(topic) {
69
58
  if (!topic) {
@@ -773,6 +762,91 @@ async function resolveEvidenceScopeOrNull(ctx, args) {
773
762
  }
774
763
 
775
764
  // src/epistemicEvidenceMutations.ts
765
+ function assertSignedImpactScore(value, context) {
766
+ if (typeof value !== "number" || !Number.isFinite(value) || value === 0 || value < -1 || value > 1) {
767
+ throw new Error(`${context} requires explicit nonzero weight in [-1, 1]`);
768
+ }
769
+ return value;
770
+ }
771
+ function normalizeEvidenceRelation(relation, weight, context) {
772
+ if (relation === "supports" || relation === "contradicts") {
773
+ if (relation === "supports" && weight < 0) {
774
+ throw new Error(`${context} supports relation requires positive weight`);
775
+ }
776
+ if (relation === "contradicts" && weight > 0) {
777
+ throw new Error(`${context} contradicts relation requires negative weight`);
778
+ }
779
+ return relation;
780
+ }
781
+ return weight < 0 ? "contradicts" : "supports";
782
+ }
783
+ async function createEvidenceBeliefEdge(ctx, args) {
784
+ const edgeGlobalId = crypto.randomUUID();
785
+ const confidence = Math.abs(args.weight);
786
+ const existingEdges = await ctx.db.query("epistemicEdges").withIndex(
787
+ "by_from_to",
788
+ (q) => q.eq("fromNodeId", args.evidenceNodeId).eq("toNodeId", args.beliefNodeId)
789
+ ).collect();
790
+ const existing = existingEdges.find((edge) => edge.edgeType === "informs");
791
+ const edgeDoc = {
792
+ globalId: edgeGlobalId,
793
+ fromNodeId: args.evidenceNodeId,
794
+ toNodeId: args.beliefNodeId,
795
+ sourceGlobalId: args.evidenceGlobalId,
796
+ targetGlobalId: args.beliefGlobalId,
797
+ edgeType: "informs",
798
+ weight: args.weight,
799
+ confidence,
800
+ context: args.rationale,
801
+ reasoningMethod: "testimonial",
802
+ derivationType: "evidence_sl_scoring",
803
+ metadata: {
804
+ relation: args.relation,
805
+ confidence,
806
+ impactScore: args.weight,
807
+ invariant: "evidence.belief_impact_required"
808
+ },
809
+ createdBy: args.userId,
810
+ createdAt: Date.now(),
811
+ updatedAt: Date.now(),
812
+ topicId: args.topicId,
813
+ projectId: args.projectId,
814
+ fromNodeType: "evidence",
815
+ toNodeType: "belief",
816
+ fromLayer: "L2",
817
+ toLayer: "L3"
818
+ };
819
+ if (existing) {
820
+ await ctx.db.patch(existing._id, {
821
+ ...edgeDoc,
822
+ globalId: existing.globalId,
823
+ createdAt: existing.createdAt ?? edgeDoc.createdAt
824
+ });
825
+ } else {
826
+ await ctx.db.insert("epistemicEdges", edgeDoc);
827
+ }
828
+ await ctx.scheduler.runAfter(5e3, internal.neo4jEdgeAPI.createEdge, {
829
+ globalId: existing?.globalId ?? edgeGlobalId,
830
+ fromGlobalId: args.evidenceGlobalId,
831
+ toGlobalId: args.beliefGlobalId,
832
+ edgeType: "informs",
833
+ weight: args.weight,
834
+ confidence,
835
+ createdBy: args.userId,
836
+ topicId: args.projectId ?? args.topicId,
837
+ fromNodeType: "evidence",
838
+ toNodeType: "belief",
839
+ fromLayer: "L2",
840
+ toLayer: "L3",
841
+ metadata: {
842
+ relation: args.relation,
843
+ confidence,
844
+ impactScore: args.weight,
845
+ invariant: "evidence.belief_impact_required"
846
+ }
847
+ });
848
+ return existing?.globalId ?? edgeGlobalId;
849
+ }
776
850
  var create = mutation({
777
851
  args: {
778
852
  ...optionalEvidenceScopeArgs,
@@ -793,11 +867,12 @@ var create = mutation({
793
867
  informationAsymmetry: v.optional(v.string()),
794
868
  sourceDescription: v.optional(v.string()),
795
869
  metadata: v.optional(v.any()),
796
- // Optional linking to beliefs
797
- linkedBeliefNodeId: v.optional(v.id("epistemicNodes")),
870
+ // Required belief impact link.
871
+ linkedBeliefNodeId: v.id("epistemicNodes"),
798
872
  evidenceRelation: v.optional(
799
873
  v.union(v.literal("supports"), v.literal("contradicts"))
800
874
  ),
875
+ weight: v.number(),
801
876
  confidence: v.optional(v.number())
802
877
  },
803
878
  returns: permissiveReturn,
@@ -819,6 +894,16 @@ var create = mutation({
819
894
  const contentHash = generateContentHash(args.text);
820
895
  const kind = normalizeKind(args.kind);
821
896
  const sourceType = normalizeSourceType(args.sourceType);
897
+ const weight = assertSignedImpactScore(args.weight, "Evidence creation");
898
+ const evidenceRelation = normalizeEvidenceRelation(
899
+ args.evidenceRelation,
900
+ weight,
901
+ "Evidence creation"
902
+ );
903
+ const linkedBeliefNode = await ctx.db.get(args.linkedBeliefNodeId);
904
+ if (!linkedBeliefNode || linkedBeliefNode.nodeType !== "belief") {
905
+ throw new Error("Evidence creation requires a linked belief node");
906
+ }
822
907
  const additionalMetadata = args.metadata && typeof args.metadata === "object" ? args.metadata : {};
823
908
  const nodeId = await ctx.db.insert("epistemicNodes", {
824
909
  globalId,
@@ -846,8 +931,10 @@ var create = mutation({
846
931
  sourceQuestionId: args.sourceQuestionId,
847
932
  rationale: args.rationale,
848
933
  linkedBeliefNodeId: args.linkedBeliefNodeId,
849
- evidenceRelation: args.evidenceRelation,
850
- confidence: args.confidence,
934
+ evidenceRelation,
935
+ confidence: Math.abs(weight),
936
+ weight,
937
+ impactScore: weight,
851
938
  methodology: args.methodology,
852
939
  informationAsymmetry: args.informationAsymmetry,
853
940
  sourceDescription: args.sourceDescription,
@@ -867,29 +954,18 @@ var create = mutation({
867
954
  nodeType: "evidence",
868
955
  text: args.text
869
956
  });
870
- if (args.linkedBeliefNodeId && args.evidenceRelation) {
871
- const beliefNode = await ctx.db.get(args.linkedBeliefNodeId);
872
- if (beliefNode) {
873
- const weight = args.evidenceRelation === "supports" ? 1 : -1;
874
- await ctx.scheduler.runAfter(5e3, internal.neo4jEdgeAPI.createEdge, {
875
- globalId: crypto.randomUUID(),
876
- fromGlobalId: globalId,
877
- toGlobalId: beliefNode.globalId,
878
- edgeType: "informs",
879
- weight: weight * (args.confidence || 0.7),
880
- createdBy: args.userId,
881
- topicId: scope.projectId ? String(scope.projectId) : void 0,
882
- fromNodeType: "evidence",
883
- toNodeType: "belief",
884
- fromLayer: "L2",
885
- toLayer: "L3",
886
- metadata: {
887
- relation: args.evidenceRelation,
888
- confidence: args.confidence
889
- }
890
- });
891
- }
892
- }
957
+ await createEvidenceBeliefEdge(ctx, {
958
+ evidenceNodeId: nodeId,
959
+ evidenceGlobalId: globalId,
960
+ beliefNodeId: args.linkedBeliefNodeId,
961
+ beliefGlobalId: linkedBeliefNode.globalId,
962
+ relation: evidenceRelation,
963
+ weight,
964
+ userId: args.userId,
965
+ topicId: scope.topicId ? String(scope.topicId) : void 0,
966
+ projectId: scope.projectId ? String(scope.projectId) : void 0,
967
+ rationale: args.rationale
968
+ });
893
969
  await ctx.db.insert("epistemicAudit", {
894
970
  entityType: "evidence",
895
971
  entityId: nodeId,
@@ -904,7 +980,8 @@ var create = mutation({
904
980
  kind,
905
981
  sourceType,
906
982
  linkedBeliefNodeId: args.linkedBeliefNodeId,
907
- evidenceRelation: args.evidenceRelation
983
+ evidenceRelation,
984
+ weight
908
985
  }
909
986
  });
910
987
  if (scope.projectId || scope.topicId) {
@@ -947,6 +1024,7 @@ var createAndLink = mutation({
947
1024
  userId: v.string(),
948
1025
  beliefNodeId: v.id("epistemicNodes"),
949
1026
  relation: v.union(v.literal("supports"), v.literal("contradicts")),
1027
+ weight: v.number(),
950
1028
  confidence: v.optional(v.number())
951
1029
  },
952
1030
  returns: permissiveReturn,
@@ -961,7 +1039,17 @@ var createAndLink = mutation({
961
1039
  const contentHash = generateContentHash(args.text);
962
1040
  const kind = normalizeKind(args.kind);
963
1041
  const sourceType = normalizeSourceType(args.sourceType);
964
- const confidence = args.confidence ?? 0.7;
1042
+ const weight = assertSignedImpactScore(args.weight, "Evidence createAndLink");
1043
+ const relation = normalizeEvidenceRelation(
1044
+ args.relation,
1045
+ weight,
1046
+ "Evidence createAndLink"
1047
+ );
1048
+ const confidence = Math.abs(weight);
1049
+ const beliefNode = await ctx.db.get(args.beliefNodeId);
1050
+ if (!beliefNode || beliefNode.nodeType !== "belief") {
1051
+ throw new Error("Belief node not found for edge creation");
1052
+ }
965
1053
  const nodeId = await ctx.db.insert("epistemicNodes", {
966
1054
  globalId,
967
1055
  topicId: scope.topicId,
@@ -981,36 +1069,26 @@ var createAndLink = mutation({
981
1069
  kind,
982
1070
  tags: args.tags || [],
983
1071
  linkedBeliefNodeId: args.beliefNodeId,
984
- evidenceRelation: args.relation,
985
- confidence
1072
+ evidenceRelation: relation,
1073
+ confidence,
1074
+ weight,
1075
+ impactScore: weight
986
1076
  }
987
1077
  });
988
1078
  await ctx.scheduler.runAfter(0, internal.neo4jSync.syncNodeToNeo4j, {
989
1079
  nodeId,
990
1080
  operation: "upsert"
991
1081
  });
992
- const beliefNode = await ctx.db.get(args.beliefNodeId);
993
- if (!beliefNode) {
994
- throw new Error("Belief node not found for edge creation");
995
- }
996
- const weight = args.relation === "supports" ? confidence : -confidence;
997
- const edgeGlobalId = crypto.randomUUID();
998
- await ctx.scheduler.runAfter(5e3, internal.neo4jEdgeAPI.createEdge, {
999
- globalId: edgeGlobalId,
1000
- fromGlobalId: globalId,
1001
- toGlobalId: beliefNode.globalId,
1002
- edgeType: "informs",
1082
+ const edgeGlobalId = await createEvidenceBeliefEdge(ctx, {
1083
+ evidenceNodeId: nodeId,
1084
+ evidenceGlobalId: globalId,
1085
+ beliefNodeId: args.beliefNodeId,
1086
+ beliefGlobalId: beliefNode.globalId,
1087
+ relation,
1003
1088
  weight,
1004
- createdBy: args.userId,
1005
- topicId: scope.projectId,
1006
- fromNodeType: "evidence",
1007
- toNodeType: "belief",
1008
- fromLayer: "L2",
1009
- toLayer: "L3",
1010
- metadata: {
1011
- relation: args.relation,
1012
- confidence
1013
- }
1089
+ userId: args.userId,
1090
+ topicId: scope.topicId ? String(scope.topicId) : void 0,
1091
+ projectId: scope.projectId ? String(scope.projectId) : void 0
1014
1092
  });
1015
1093
  await markProjectGraphDirty(ctx, scope.projectId, String(scope.topicId));
1016
1094
  return { nodeId, edgeGlobalId };
@@ -1071,8 +1149,9 @@ var internalCreate = internalMutation({
1071
1149
  sourceQuestionId: v.optional(v.string()),
1072
1150
  userId: v.string(),
1073
1151
  rationale: v.string(),
1074
- linkedBeliefNodeId: v.optional(v.id("epistemicNodes")),
1152
+ linkedBeliefNodeId: v.id("epistemicNodes"),
1075
1153
  evidenceRelation: v.optional(v.string()),
1154
+ weight: v.number(),
1076
1155
  confidence: v.optional(v.number()),
1077
1156
  metadata: v.optional(v.any()),
1078
1157
  runtimeToolName: v.optional(v.string()),
@@ -1107,6 +1186,16 @@ var internalCreate = internalMutation({
1107
1186
  const contentHash = generateContentHash(args.text);
1108
1187
  const kind = normalizeKind(args.kind);
1109
1188
  const sourceType = normalizeSourceType(args.sourceType);
1189
+ const weight = assertSignedImpactScore(args.weight, "Internal evidence creation");
1190
+ const evidenceRelation = normalizeEvidenceRelation(
1191
+ args.evidenceRelation,
1192
+ weight,
1193
+ "Internal evidence creation"
1194
+ );
1195
+ const linkedBeliefNode = await ctx.db.get(args.linkedBeliefNodeId);
1196
+ if (!linkedBeliefNode || linkedBeliefNode.nodeType !== "belief") {
1197
+ throw new Error("Internal evidence creation requires a linked belief node");
1198
+ }
1110
1199
  const additionalMetadata = args.metadata && typeof args.metadata === "object" ? args.metadata : {};
1111
1200
  const nodeId = await ctx.db.insert("epistemicNodes", {
1112
1201
  globalId,
@@ -1134,8 +1223,10 @@ var internalCreate = internalMutation({
1134
1223
  sourceQuestionId: args.sourceQuestionId,
1135
1224
  rationale: args.rationale,
1136
1225
  linkedBeliefNodeId: args.linkedBeliefNodeId,
1137
- evidenceRelation: args.evidenceRelation,
1138
- confidence: args.confidence,
1226
+ evidenceRelation,
1227
+ confidence: Math.abs(weight),
1228
+ weight,
1229
+ impactScore: weight,
1139
1230
  ...additionalMetadata
1140
1231
  }
1141
1232
  });
@@ -1155,8 +1246,9 @@ var internalCreate = internalMutation({
1155
1246
  externalSourceType: args.externalSourceType,
1156
1247
  sourceUrl: args.sourceUrl,
1157
1248
  linkedBeliefNodeId: args.linkedBeliefNodeId,
1158
- evidenceRelation: args.evidenceRelation,
1159
- confidence: args.confidence
1249
+ evidenceRelation,
1250
+ confidence: Math.abs(weight),
1251
+ weight
1160
1252
  },
1161
1253
  triggeringAction: "epistemicEvidence.internalCreate"
1162
1254
  });
@@ -1164,30 +1256,18 @@ var internalCreate = internalMutation({
1164
1256
  nodeId,
1165
1257
  operation: "upsert"
1166
1258
  });
1167
- if (args.linkedBeliefNodeId && args.evidenceRelation) {
1168
- const beliefNode = await ctx.db.get(args.linkedBeliefNodeId);
1169
- if (beliefNode) {
1170
- const confidence = args.confidence ?? 0.7;
1171
- const weight = args.evidenceRelation === "supports" ? confidence : -confidence;
1172
- await ctx.scheduler.runAfter(5e3, internal.neo4jEdgeAPI.createEdge, {
1173
- globalId: crypto.randomUUID(),
1174
- fromGlobalId: globalId,
1175
- toGlobalId: beliefNode.globalId,
1176
- edgeType: "informs",
1177
- weight,
1178
- createdBy: args.userId,
1179
- topicId: scope.projectId ? String(scope.projectId) : void 0,
1180
- fromNodeType: "evidence",
1181
- toNodeType: "belief",
1182
- fromLayer: "L2",
1183
- toLayer: "L3",
1184
- metadata: {
1185
- relation: args.evidenceRelation,
1186
- confidence
1187
- }
1188
- });
1189
- }
1190
- }
1259
+ await createEvidenceBeliefEdge(ctx, {
1260
+ evidenceNodeId: nodeId,
1261
+ evidenceGlobalId: globalId,
1262
+ beliefNodeId: args.linkedBeliefNodeId,
1263
+ beliefGlobalId: linkedBeliefNode.globalId,
1264
+ relation: evidenceRelation,
1265
+ weight,
1266
+ userId: args.userId,
1267
+ topicId: scope.topicId ? String(scope.topicId) : void 0,
1268
+ projectId: scope.projectId ? String(scope.projectId) : void 0,
1269
+ rationale: args.rationale
1270
+ });
1191
1271
  if (scope.projectId || scope.topicId) {
1192
1272
  await ctx.scheduler.runAfter(
1193
1273
  0,