@memberjunction/ng-dashboards 6.2.0-edge.4 → 6.2.0-edge.6

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 (205) hide show
  1. package/README.md +28 -0
  2. package/dist/AI/components/autotagging/autotagging-pipeline-resource.component.d.ts +1 -1
  3. package/dist/AI/components/autotagging/autotagging-pipeline-resource.component.d.ts.map +1 -1
  4. package/dist/AI/components/autotagging/autotagging-pipeline-resource.component.js +15 -7
  5. package/dist/AI/components/autotagging/autotagging-pipeline-resource.component.js.map +1 -1
  6. package/dist/AI/components/duplicates/duplicate-detection-resource.component.d.ts +7 -1
  7. package/dist/AI/components/duplicates/duplicate-detection-resource.component.d.ts.map +1 -1
  8. package/dist/AI/components/duplicates/duplicate-detection-resource.component.js +209 -57
  9. package/dist/AI/components/duplicates/duplicate-detection-resource.component.js.map +1 -1
  10. package/dist/AI/components/duplicates/duplicate-detection-run-scope.d.ts +12 -11
  11. package/dist/AI/components/duplicates/duplicate-detection-run-scope.d.ts.map +1 -1
  12. package/dist/AI/components/duplicates/duplicate-detection-run-scope.js +15 -15
  13. package/dist/AI/components/duplicates/duplicate-detection-run-scope.js.map +1 -1
  14. package/dist/AI/components/execution-monitoring.component.d.ts.map +1 -1
  15. package/dist/AI/components/execution-monitoring.component.js +28 -16
  16. package/dist/AI/components/execution-monitoring.component.js.map +1 -1
  17. package/dist/AI/components/prompts/model-prompt-priority-matrix.component.d.ts +1 -0
  18. package/dist/AI/components/prompts/model-prompt-priority-matrix.component.d.ts.map +1 -1
  19. package/dist/AI/components/prompts/model-prompt-priority-matrix.component.js +30 -6
  20. package/dist/AI/components/prompts/model-prompt-priority-matrix.component.js.map +1 -1
  21. package/dist/AI/components/tags/tags-resource.component.d.ts +1 -1
  22. package/dist/AI/components/tags/tags-resource.component.d.ts.map +1 -1
  23. package/dist/AI/components/tags/tags-resource.component.js +15 -7
  24. package/dist/AI/components/tags/tags-resource.component.js.map +1 -1
  25. package/dist/AI/components/vectors/sensitive-fields.d.ts +58 -0
  26. package/dist/AI/components/vectors/sensitive-fields.d.ts.map +1 -0
  27. package/dist/AI/components/vectors/sensitive-fields.js +251 -0
  28. package/dist/AI/components/vectors/sensitive-fields.js.map +1 -0
  29. package/dist/AI/components/vectors/vector-document-rules.d.ts +139 -0
  30. package/dist/AI/components/vectors/vector-document-rules.d.ts.map +1 -0
  31. package/dist/AI/components/vectors/vector-document-rules.js +182 -0
  32. package/dist/AI/components/vectors/vector-document-rules.js.map +1 -0
  33. package/dist/AI/components/vectors/vector-management-resource.component.d.ts +83 -14
  34. package/dist/AI/components/vectors/vector-management-resource.component.d.ts.map +1 -1
  35. package/dist/AI/components/vectors/vector-management-resource.component.js +546 -271
  36. package/dist/AI/components/vectors/vector-management-resource.component.js.map +1 -1
  37. package/dist/AI/components/vectors/vector-store-selection.d.ts +32 -0
  38. package/dist/AI/components/vectors/vector-store-selection.d.ts.map +1 -0
  39. package/dist/AI/components/vectors/vector-store-selection.js +57 -0
  40. package/dist/AI/components/vectors/vector-store-selection.js.map +1 -0
  41. package/dist/ChiefOfStaff/chief-of-staff-agent-context.d.ts +236 -0
  42. package/dist/ChiefOfStaff/chief-of-staff-agent-context.d.ts.map +1 -0
  43. package/dist/ChiefOfStaff/chief-of-staff-agent-context.js +374 -0
  44. package/dist/ChiefOfStaff/chief-of-staff-agent-context.js.map +1 -0
  45. package/dist/ChiefOfStaff/chief-of-staff-dashboard.component.d.ts +195 -0
  46. package/dist/ChiefOfStaff/chief-of-staff-dashboard.component.d.ts.map +1 -0
  47. package/dist/ChiefOfStaff/chief-of-staff-dashboard.component.js +2910 -0
  48. package/dist/ChiefOfStaff/chief-of-staff-dashboard.component.js.map +1 -0
  49. package/dist/ChiefOfStaff/index.d.ts +3 -0
  50. package/dist/ChiefOfStaff/index.d.ts.map +1 -0
  51. package/dist/ChiefOfStaff/index.js +3 -0
  52. package/dist/ChiefOfStaff/index.js.map +1 -0
  53. package/dist/Communication/communication-agent-context.d.ts +11 -1
  54. package/dist/Communication/communication-agent-context.d.ts.map +1 -1
  55. package/dist/Communication/communication-agent-context.js +15 -0
  56. package/dist/Communication/communication-agent-context.js.map +1 -1
  57. package/dist/Communication/communication-dashboard.component.d.ts.map +1 -1
  58. package/dist/Communication/communication-dashboard.component.js +5 -0
  59. package/dist/Communication/communication-dashboard.component.js.map +1 -1
  60. package/dist/Communication/communication-logs-resource.component.d.ts.map +1 -1
  61. package/dist/Communication/communication-logs-resource.component.js +2 -1
  62. package/dist/Communication/communication-logs-resource.component.js.map +1 -1
  63. package/dist/Communication/communication-monitor-resource.component.d.ts.map +1 -1
  64. package/dist/Communication/communication-monitor-resource.component.js +23 -18
  65. package/dist/Communication/communication-monitor-resource.component.js.map +1 -1
  66. package/dist/Communication/communication-providers-resource.component.d.ts +27 -1
  67. package/dist/Communication/communication-providers-resource.component.d.ts.map +1 -1
  68. package/dist/Communication/communication-providers-resource.component.js +722 -78
  69. package/dist/Communication/communication-providers-resource.component.js.map +1 -1
  70. package/dist/Communication/communication-runs-resource.component.d.ts.map +1 -1
  71. package/dist/Communication/communication-runs-resource.component.js +11 -10
  72. package/dist/Communication/communication-runs-resource.component.js.map +1 -1
  73. package/dist/DashboardEditor/dashboard-editor.component.d.ts +17 -2
  74. package/dist/DashboardEditor/dashboard-editor.component.d.ts.map +1 -1
  75. package/dist/DashboardEditor/dashboard-editor.component.js +94 -43
  76. package/dist/DashboardEditor/dashboard-editor.component.js.map +1 -1
  77. package/dist/DataExplorer/data-explorer-dashboard.component.d.ts +49 -0
  78. package/dist/DataExplorer/data-explorer-dashboard.component.d.ts.map +1 -1
  79. package/dist/DataExplorer/data-explorer-dashboard.component.js +220 -55
  80. package/dist/DataExplorer/data-explorer-dashboard.component.js.map +1 -1
  81. package/dist/DataExplorer/models/explorer-state.interface.d.ts +2 -0
  82. package/dist/DataExplorer/models/explorer-state.interface.d.ts.map +1 -1
  83. package/dist/DataExplorer/models/explorer-state.interface.js.map +1 -1
  84. package/dist/DevTools/app-state-inspector.component.d.ts.map +1 -1
  85. package/dist/DevTools/app-state-inspector.component.js +1 -0
  86. package/dist/DevTools/app-state-inspector.component.js.map +1 -1
  87. package/dist/DevTools/class-registry.component.d.ts.map +1 -1
  88. package/dist/DevTools/class-registry.component.js +1 -0
  89. package/dist/DevTools/class-registry.component.js.map +1 -1
  90. package/dist/DevTools/event-monitor.component.d.ts.map +1 -1
  91. package/dist/DevTools/event-monitor.component.js +2 -0
  92. package/dist/DevTools/event-monitor.component.js.map +1 -1
  93. package/dist/DevTools/graphql-console.component.d.ts.map +1 -1
  94. package/dist/DevTools/graphql-console.component.js.map +1 -1
  95. package/dist/DevTools/grid-width-lab.component.d.ts.map +1 -1
  96. package/dist/DevTools/grid-width-lab.component.js +1 -0
  97. package/dist/DevTools/grid-width-lab.component.js.map +1 -1
  98. package/dist/DevTools/layout-inspector.component.d.ts.map +1 -1
  99. package/dist/DevTools/layout-inspector.component.js +1 -0
  100. package/dist/DevTools/layout-inspector.component.js.map +1 -1
  101. package/dist/DevTools/lazy-module-status.component.d.ts.map +1 -1
  102. package/dist/DevTools/lazy-module-status.component.js +1 -0
  103. package/dist/DevTools/lazy-module-status.component.js.map +1 -1
  104. package/dist/DevTools/settings-explorer.component.d.ts.map +1 -1
  105. package/dist/DevTools/settings-explorer.component.js +1 -0
  106. package/dist/DevTools/settings-explorer.component.js.map +1 -1
  107. package/dist/DevTools/tab-strip-lab.component.d.ts.map +1 -1
  108. package/dist/DevTools/tab-strip-lab.component.js +1 -0
  109. package/dist/DevTools/tab-strip-lab.component.js.map +1 -1
  110. package/dist/EntityAdmin/entity-admin-dashboard.component.d.ts +5 -0
  111. package/dist/EntityAdmin/entity-admin-dashboard.component.d.ts.map +1 -1
  112. package/dist/EntityAdmin/entity-admin-dashboard.component.js +8 -5
  113. package/dist/EntityAdmin/entity-admin-dashboard.component.js.map +1 -1
  114. package/dist/Integration/components/overview/overview.component.js +2 -2
  115. package/dist/KnowledgeHub/components/config/knowledge-config-resource.component.js +2 -2
  116. package/dist/KnowledgeHub/components/config/knowledge-config-resource.component.js.map +1 -1
  117. package/dist/KnowledgeHub/components/feature-pipelines/feature-pipeline.engine.d.ts +11 -1
  118. package/dist/KnowledgeHub/components/feature-pipelines/feature-pipeline.engine.d.ts.map +1 -1
  119. package/dist/KnowledgeHub/components/feature-pipelines/feature-pipeline.engine.js +24 -2
  120. package/dist/KnowledgeHub/components/feature-pipelines/feature-pipeline.engine.js.map +1 -1
  121. package/dist/KnowledgeHub/components/feature-pipelines/feature-pipelines-resource.component.d.ts +16 -2
  122. package/dist/KnowledgeHub/components/feature-pipelines/feature-pipelines-resource.component.d.ts.map +1 -1
  123. package/dist/KnowledgeHub/components/feature-pipelines/feature-pipelines-resource.component.js +87 -33
  124. package/dist/KnowledgeHub/components/feature-pipelines/feature-pipelines-resource.component.js.map +1 -1
  125. package/dist/LoginStudio/login-studio-inspector.component.d.ts +130 -0
  126. package/dist/LoginStudio/login-studio-inspector.component.d.ts.map +1 -0
  127. package/dist/LoginStudio/login-studio-inspector.component.js +781 -0
  128. package/dist/LoginStudio/login-studio-inspector.component.js.map +1 -0
  129. package/dist/LoginStudio/login-studio-resource.component.d.ts +161 -0
  130. package/dist/LoginStudio/login-studio-resource.component.d.ts.map +1 -0
  131. package/dist/LoginStudio/login-studio-resource.component.js +890 -0
  132. package/dist/LoginStudio/login-studio-resource.component.js.map +1 -0
  133. package/dist/MCP/mcp-dashboard.component.d.ts +24 -4
  134. package/dist/MCP/mcp-dashboard.component.d.ts.map +1 -1
  135. package/dist/MCP/mcp-dashboard.component.js +118 -65
  136. package/dist/MCP/mcp-dashboard.component.js.map +1 -1
  137. package/dist/PredictiveStudio/business-predictions.view-models.d.ts +1 -1
  138. package/dist/PredictiveStudio/business-predictions.view-models.d.ts.map +1 -1
  139. package/dist/PredictiveStudio/business-predictions.view-models.js +3 -3
  140. package/dist/PredictiveStudio/business-predictions.view-models.js.map +1 -1
  141. package/dist/PredictiveStudio/components/ps-experiments.component.d.ts +6 -63
  142. package/dist/PredictiveStudio/components/ps-experiments.component.d.ts.map +1 -1
  143. package/dist/PredictiveStudio/components/ps-experiments.component.js +256 -521
  144. package/dist/PredictiveStudio/components/ps-experiments.component.js.map +1 -1
  145. package/dist/PredictiveStudio/components/ps-pipeline-wizard.component.d.ts +0 -18
  146. package/dist/PredictiveStudio/components/ps-pipeline-wizard.component.d.ts.map +1 -1
  147. package/dist/PredictiveStudio/components/ps-pipeline-wizard.component.js +122 -249
  148. package/dist/PredictiveStudio/components/ps-pipeline-wizard.component.js.map +1 -1
  149. package/dist/PredictiveStudio/components/ps-pipelines.component.d.ts +5 -9
  150. package/dist/PredictiveStudio/components/ps-pipelines.component.d.ts.map +1 -1
  151. package/dist/PredictiveStudio/components/ps-pipelines.component.js +98 -130
  152. package/dist/PredictiveStudio/components/ps-pipelines.component.js.map +1 -1
  153. package/dist/PredictiveStudio/components/ps-predictions-grid.component.d.ts +53 -4
  154. package/dist/PredictiveStudio/components/ps-predictions-grid.component.d.ts.map +1 -1
  155. package/dist/PredictiveStudio/components/ps-predictions-grid.component.js +335 -326
  156. package/dist/PredictiveStudio/components/ps-predictions-grid.component.js.map +1 -1
  157. package/dist/PredictiveStudio/components/ps-row-drivers.component.d.ts +19 -0
  158. package/dist/PredictiveStudio/components/ps-row-drivers.component.d.ts.map +1 -0
  159. package/dist/PredictiveStudio/components/ps-row-drivers.component.js +85 -0
  160. package/dist/PredictiveStudio/components/ps-row-drivers.component.js.map +1 -0
  161. package/dist/PredictiveStudio/engine/predictive-studio.engine.d.ts +19 -1
  162. package/dist/PredictiveStudio/engine/predictive-studio.engine.d.ts.map +1 -1
  163. package/dist/PredictiveStudio/engine/predictive-studio.engine.js +51 -2
  164. package/dist/PredictiveStudio/engine/predictive-studio.engine.js.map +1 -1
  165. package/dist/PredictiveStudio/predictive-studio-agent-context.d.ts +3 -1
  166. package/dist/PredictiveStudio/predictive-studio-agent-context.d.ts.map +1 -1
  167. package/dist/PredictiveStudio/predictive-studio-agent-context.js +3 -0
  168. package/dist/PredictiveStudio/predictive-studio-agent-context.js.map +1 -1
  169. package/dist/PredictiveStudio/predictive-studio-score-history.service.d.ts +27 -4
  170. package/dist/PredictiveStudio/predictive-studio-score-history.service.d.ts.map +1 -1
  171. package/dist/PredictiveStudio/predictive-studio-score-history.service.js +94 -61
  172. package/dist/PredictiveStudio/predictive-studio-score-history.service.js.map +1 -1
  173. package/dist/PredictiveStudio/resources/ps-predictions-resource.component.d.ts +29 -5
  174. package/dist/PredictiveStudio/resources/ps-predictions-resource.component.d.ts.map +1 -1
  175. package/dist/PredictiveStudio/resources/ps-predictions-resource.component.js +185 -103
  176. package/dist/PredictiveStudio/resources/ps-predictions-resource.component.js.map +1 -1
  177. package/dist/PredictiveStudio/resources/ps-studio-resource.component.js +1 -1
  178. package/dist/PredictiveStudio/resources/ps-studio-resource.component.js.map +1 -1
  179. package/dist/Workflows/components/workflow-runs-resource.component.js +4 -4
  180. package/dist/Workflows/components/workflow-runs-resource.component.js.map +1 -1
  181. package/dist/ai-dashboards.module.d.ts +22 -21
  182. package/dist/ai-dashboards.module.d.ts.map +1 -1
  183. package/dist/ai-dashboards.module.js +8 -2
  184. package/dist/ai-dashboards.module.js.map +1 -1
  185. package/dist/core-dashboards.module.d.ts +2 -1
  186. package/dist/core-dashboards.module.d.ts.map +1 -1
  187. package/dist/core-dashboards.module.js +11 -3
  188. package/dist/core-dashboards.module.js.map +1 -1
  189. package/dist/public-api.d.ts +3 -0
  190. package/dist/public-api.d.ts.map +1 -1
  191. package/dist/public-api.js +4 -0
  192. package/dist/public-api.js.map +1 -1
  193. package/package.json +72 -69
  194. package/dist/PredictiveStudio/at-risk.view-models.d.ts +0 -120
  195. package/dist/PredictiveStudio/at-risk.view-models.d.ts.map +0 -1
  196. package/dist/PredictiveStudio/at-risk.view-models.js +0 -261
  197. package/dist/PredictiveStudio/at-risk.view-models.js.map +0 -1
  198. package/dist/PredictiveStudio/components/ps-confirm-modal.component.d.ts +0 -109
  199. package/dist/PredictiveStudio/components/ps-confirm-modal.component.d.ts.map +0 -1
  200. package/dist/PredictiveStudio/components/ps-confirm-modal.component.js +0 -323
  201. package/dist/PredictiveStudio/components/ps-confirm-modal.component.js.map +0 -1
  202. package/dist/PredictiveStudio/components/ps-run-experiment-modal.component.d.ts +0 -137
  203. package/dist/PredictiveStudio/components/ps-run-experiment-modal.component.d.ts.map +0 -1
  204. package/dist/PredictiveStudio/components/ps-run-experiment-modal.component.js +0 -826
  205. package/dist/PredictiveStudio/components/ps-run-experiment-modal.component.js.map +0 -1
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Fields that must never be embedded into a vector index.
3
+ *
4
+ * Vectorizing a column copies its values into a third-party vector database, where they
5
+ * live outside the entity permissions that normally guard them and are retrievable by
6
+ * nearest-neighbour search rather than by an authorized query. For a password hash, a
7
+ * national insurance or social security number, a bank account or a salary, that is not a
8
+ * feature with a caveat — it is a disclosure.
9
+ *
10
+ * The field picker's suggestions come from a language model, which has no notion of which
11
+ * columns are sensitive and will happily propose `PasswordHash` if the use case seems to
12
+ * call for identity matching. So the refusal cannot live in the prompt: it has to be a
13
+ * property of the code on both sides of the model.
14
+ *
15
+ * Two layers, deliberately:
16
+ *
17
+ * 1. The model never sees these fields, on the entity or on its related entities, so it
18
+ * cannot suggest them.
19
+ * 2. The saved template is re-checked, because the template is hand-editable after the
20
+ * model returns and a user can type a field name the picker never offered.
21
+ *
22
+ * Layer 1 alone would be a prompt-shaped guarantee, which is no guarantee at all.
23
+ */
24
+ /**
25
+ * What the vectors are for. A `'duplicate-detection'` document may embed a date of birth (see
26
+ * {@link BIRTH_DATE_FIELD_PATTERNS}); every other refusal applies to every purpose.
27
+ */
28
+ export type VectorFieldPurpose = 'duplicate-detection' | 'other';
29
+ /**
30
+ * True when this field name must not be embedded.
31
+ *
32
+ * Note `hash` and `auth` are deliberately broad. A column called `AuthorName` normalises to
33
+ * `authorname`, which contains `auth` — so it is refused. That is the trade accepted above:
34
+ * a visible false refusal the user can work around beats a silent disclosure they cannot.
35
+ */
36
+ export declare function IsSensitiveFieldName(name: string | null | undefined, purpose?: VectorFieldPurpose): boolean;
37
+ /** Drops every field that must not be embedded for this purpose, preserving order. */
38
+ export declare function WithoutSensitiveFields<T extends {
39
+ Name: string;
40
+ }>(fields: ReadonlyArray<T>, purpose?: VectorFieldPurpose): T[];
41
+ /**
42
+ * Sensitive field names referenced by a template's `{{Placeholder}}` tokens.
43
+ *
44
+ * This is the second layer. The template is hand-editable after the model returns, so a
45
+ * user can type a field the picker never offered. Returns the offending names so the
46
+ * refusal can say which ones, rather than only that something was wrong.
47
+ */
48
+ export declare function SensitiveFieldsInTemplate(templateText: string | null | undefined, purpose?: VectorFieldPurpose): string[];
49
+ /**
50
+ * Field names referenced by a template's `{{Placeholder}}` tokens, in order of appearance
51
+ * and including repeats.
52
+ *
53
+ * Exported so the field-count cap in `vector-document-rules.ts` parses placeholders the same
54
+ * way this refusal does. Two parsers that drift apart would mean a template that satisfies
55
+ * one guard and evades the other.
56
+ */
57
+ export declare function TemplatePlaceholderNames(templateText: string | null | undefined): string[];
58
+ //# sourceMappingURL=sensitive-fields.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sensitive-fields.d.ts","sourceRoot":"","sources":["../../../../src/AI/components/vectors/sensitive-fields.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AA0DH;;;GAGG;AACH,MAAM,MAAM,kBAAkB,GAAG,qBAAqB,GAAG,OAAO,CAAC;AAkHjE;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,OAAO,GAAE,kBAA4B,GAAG,OAAO,CAkBpH;AAED,sFAAsF;AACtF,wBAAgB,sBAAsB,CAAC,CAAC,SAAS;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,EAAE,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC,EAAE,OAAO,GAAE,kBAA4B,GAAG,CAAC,EAAE,CAEvI;AAED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,OAAO,GAAE,kBAA4B,GAAG,MAAM,EAAE,CAQlI;AAED;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,EAAE,CAa1F"}
@@ -0,0 +1,251 @@
1
+ /**
2
+ * Fields that must never be embedded into a vector index.
3
+ *
4
+ * Vectorizing a column copies its values into a third-party vector database, where they
5
+ * live outside the entity permissions that normally guard them and are retrievable by
6
+ * nearest-neighbour search rather than by an authorized query. For a password hash, a
7
+ * national insurance or social security number, a bank account or a salary, that is not a
8
+ * feature with a caveat — it is a disclosure.
9
+ *
10
+ * The field picker's suggestions come from a language model, which has no notion of which
11
+ * columns are sensitive and will happily propose `PasswordHash` if the use case seems to
12
+ * call for identity matching. So the refusal cannot live in the prompt: it has to be a
13
+ * property of the code on both sides of the model.
14
+ *
15
+ * Two layers, deliberately:
16
+ *
17
+ * 1. The model never sees these fields, on the entity or on its related entities, so it
18
+ * cannot suggest them.
19
+ * 2. The saved template is re-checked, because the template is hand-editable after the
20
+ * model returns and a user can type a field name the picker never offered.
21
+ *
22
+ * Layer 1 alone would be a prompt-shaped guarantee, which is no guarantee at all.
23
+ */
24
+ /**
25
+ * Substrings that mark a column as unembeddable, matched case-insensitively against the
26
+ * field name with separators ignored — so `password_hash`, `PasswordHash` and `PASSWORD`
27
+ * all match the same rule.
28
+ *
29
+ * These are substrings rather than whole names on purpose. Real schemas spell the same
30
+ * concept a dozen ways (`SSNumber`, `employee_ssn`, `ClientSecretEncrypted`), and the cost
31
+ * of the two kinds of mistake is not symmetric: refusing a harmless column produces a
32
+ * visible, correctable annoyance, while embedding a sensitive one is silent and, once
33
+ * written to the index, not fully reversible.
34
+ *
35
+ * That asymmetry justifies over-refusing a column that RESEMBLES the concept — `AuthorName`
36
+ * contains `auth`, and `auth` really is the prefix of authentication, so the resemblance
37
+ * carries some signal and refusing it is a trade we accept. It does not justify a match
38
+ * with no semantic relationship at all. `Rating` is not refused because it looks like a tax
39
+ * identifier; it is refused because the letters `t-i-n` happen to sit inside an unrelated
40
+ * English word, which trades away a usable column for no signal whatsoever. Patterns of
41
+ * that shape belong in {@link SENSITIVE_FIELD_WORD_PATTERNS} or nowhere — see
42
+ * `vector-sensitive-fields.test.ts`, which pins the refusal count over MJ's own field names
43
+ * so that re-adding one is a red test rather than a quiet doubling of the blast radius.
44
+ */
45
+ const SENSITIVE_FIELD_PATTERNS = [
46
+ // Credentials and secrets. `token` is NOT here on its own: MJ alone has 36 columns that
47
+ // are token *counts* (`ContextWindowMaxTokens`, `InputTokenLimit`, `FirstTokenTime`),
48
+ // and a count is not a secret. The compounds below keep the credentials; `AuthToken`
49
+ // and `OAuthToken` are still caught by `auth`.
50
+ 'password', 'passwd', 'pwd', 'secret', 'apikey', 'accesskey', 'privatekey',
51
+ 'accesstoken', 'refreshtoken', 'apitoken', 'bearertoken', 'sessiontoken',
52
+ 'salt', 'hash', 'credential', 'auth', 'connectionstring', 'passcode', 'biometric',
53
+ // Government and tax identifiers. `tin` is deliberately absent: `taxid` already covers
54
+ // the concept, while `t-i-n` is a substring of ordinary English (Ra-tin-g, Marke-tin-g,
55
+ // Set-tin-gs, Mee-tin-g) and matched 43 MJ columns, none of them a tax identifier.
56
+ // A whole-word `tin` is in SENSITIVE_FIELD_WHOLE_WORD_PATTERNS below. `pin` and `otp`
57
+ // are whole-word for the same reason; `passcode` and `biometric` are long enough to be
58
+ // safe as substrings.
59
+ 'socialsec', 'nationalinsurance', 'taxid', 'passport', 'driverlicense', 'driverslicense',
60
+ // Financial
61
+ 'bank', 'routing', 'iban', 'swift', 'accountnumber', 'cardnumber', 'cardnum', 'cvv',
62
+ 'cvc', 'creditcard', 'salary', 'compensation', 'income',
63
+ // Protected characteristics and other special-category data
64
+ 'ethnic', 'religion', 'political', 'sexualorientation', 'disability',
65
+ 'healthcondition', 'diagnosis', 'medicalrecord',
66
+ ];
67
+ /**
68
+ * Date of birth: refused like the patterns above, except in a document built for duplicate
69
+ * detection. Telling two records for the same person apart from two people who share a name
70
+ * is what a birth date is for, and without it duplicate detection on people loses most of its
71
+ * precision. Every other purpose (search, classification) still refuses it.
72
+ *
73
+ * `dateofbirth` and `birthdate` match anywhere in the name; `dob` must begin a word, for the
74
+ * reason given at {@link SENSITIVE_FIELD_WORD_PATTERNS}.
75
+ */
76
+ const BIRTH_DATE_FIELD_PATTERNS = ['dateofbirth', 'birthdate'];
77
+ const BIRTH_DATE_FIELD_WORD_PATTERNS = ['dob'];
78
+ /**
79
+ * Patterns that must BEGIN A WORD in the field name rather than appear anywhere in it.
80
+ *
81
+ * These three are short enough that as bare substrings they collide with unrelated words
82
+ * and catch nothing real: `ssn` is inside `cla-ssn-ame` (every `*ClassName` column in MJ),
83
+ * `dob` is inside `recor-dob-ject`, `race` is inside `StackT-race` and `T-race-ID`. Each of
84
+ * those matches carries zero information about sensitivity, and because the template check
85
+ * throws, a user who needs `{{Rating}}` or `{{StackTrace}}` has no workaround at all — it
86
+ * is a feature they cannot use, not an annoyance they route around.
87
+ *
88
+ * Anchoring loses no true positives, because all three are whole words wherever they are
89
+ * meant: `ssn`, `employee_ssn`, `EmployeeSSN`, `SSNumber`, `dob`, `DOB`, `race`,
90
+ * `EmployeeRace`. (`dob` itself lives in {@link BIRTH_DATE_FIELD_WORD_PATTERNS}, because duplicate
91
+ * detection may use it.)
92
+ *
93
+ * `idtoken` is here for the same reason: as a substring it sits inside `Pa-idToken-s` and
94
+ * `Val-idToken-Count`, which are counts, while `IdToken`, `IDToken` and `id_token` all begin
95
+ * a word.
96
+ */
97
+ const SENSITIVE_FIELD_WORD_PATTERNS = ['ssn', 'race', 'idtoken'];
98
+ /**
99
+ * Patterns that must be a WHOLE WORD: begin a word and end at the end of the name or where
100
+ * the next word begins.
101
+ *
102
+ * `tin` (tax identification number) is too short even for a word start: `Tint`, `TinyInt`
103
+ * and `Tinker` all begin with it. As a word of its own it means one thing: `TIN`,
104
+ * `VendorTIN`, `vendor_tin`, `TINNumber`.
105
+ *
106
+ * `pin` (a personal identification number) and `otp` (a one-time password) are whole words
107
+ * for the same reason: `Shipping`, `Mapping`, `Pinned` and `Hotpot` contain them. `UserPin`,
108
+ * `PIN` and `OTPCode` are refused; `Pinned` and `PinnedAt` are not.
109
+ */
110
+ const SENSITIVE_FIELD_WHOLE_WORD_PATTERNS = ['tin', 'pin', 'otp'];
111
+ /** Characters dropped by normalisation. The two spellings must stay in step. */
112
+ const SEPARATORS_GLOBAL = /[\s_\-.]/g;
113
+ const SEPARATOR = /[\s_\-.]/;
114
+ const LOWER_OR_DIGIT = /[a-z0-9]/;
115
+ const UPPER = /[A-Z]/;
116
+ const LOWER = /[a-z]/;
117
+ /** Normalises a field name for matching: lower-cased, separators and spaces removed. */
118
+ function normalizeFieldName(name) {
119
+ return name.toLowerCase().replace(SEPARATORS_GLOBAL, '');
120
+ }
121
+ /**
122
+ * The offsets in `normalizeFieldName(name)` at which a word begins.
123
+ *
124
+ * Normalisation *removes* separators rather than replacing them, so by the time a pattern is
125
+ * matched there is no separator left to anchor a `\b` against — `employee_ssn` and
126
+ * `className` are both one unbroken run of letters (`employeessn`, `classname`). A regex word
127
+ * boundary would therefore match at offset 0 and nowhere else, which is not the boundary we
128
+ * need. So the word starts are computed from the ORIGINAL spelling, where the evidence still
129
+ * exists, and carried as offsets into the normalised string.
130
+ *
131
+ * A word starts at the beginning, after a separator, and at the two camelCase transitions:
132
+ * lower-or-digit followed by upper (`employee|SSN`), and the tail of an acronym run that
133
+ * begins a capitalised word (`SS|Number`, so `SSNumber` still matches `ssn`).
134
+ */
135
+ function wordStartOffsets(name) {
136
+ const starts = new Set();
137
+ let offset = 0;
138
+ let startsWord = true;
139
+ for (let i = 0; i < name.length; i++) {
140
+ const ch = name[i];
141
+ if (SEPARATOR.test(ch)) {
142
+ // Dropped by normalizeFieldName, so it consumes no offset — but whatever
143
+ // survives next begins a word.
144
+ startsWord = true;
145
+ continue;
146
+ }
147
+ const prev = i > 0 ? name[i - 1] : '';
148
+ const next = i + 1 < name.length ? name[i + 1] : '';
149
+ if (UPPER.test(ch) && (LOWER_OR_DIGIT.test(prev) || (UPPER.test(prev) && LOWER.test(next)))) {
150
+ startsWord = true;
151
+ }
152
+ if (startsWord) {
153
+ starts.add(offset);
154
+ }
155
+ startsWord = false;
156
+ offset++;
157
+ }
158
+ return starts;
159
+ }
160
+ /** True when `pattern` occurs in `normalized` starting at one of `wordStarts`. */
161
+ function matchesAtWordStart(normalized, wordStarts, pattern) {
162
+ let at = normalized.indexOf(pattern);
163
+ while (at !== -1) {
164
+ if (wordStarts.has(at)) {
165
+ return true;
166
+ }
167
+ at = normalized.indexOf(pattern, at + 1);
168
+ }
169
+ return false;
170
+ }
171
+ /** True when `pattern` occurs in `normalized` as a whole word, by the offsets in `wordStarts`. */
172
+ function matchesWholeWord(normalized, wordStarts, pattern) {
173
+ let at = normalized.indexOf(pattern);
174
+ while (at !== -1) {
175
+ const end = at + pattern.length;
176
+ if (wordStarts.has(at) && (end === normalized.length || wordStarts.has(end))) {
177
+ return true;
178
+ }
179
+ at = normalized.indexOf(pattern, at + 1);
180
+ }
181
+ return false;
182
+ }
183
+ /**
184
+ * True when this field name must not be embedded.
185
+ *
186
+ * Note `hash` and `auth` are deliberately broad. A column called `AuthorName` normalises to
187
+ * `authorname`, which contains `auth` — so it is refused. That is the trade accepted above:
188
+ * a visible false refusal the user can work around beats a silent disclosure they cannot.
189
+ */
190
+ export function IsSensitiveFieldName(name, purpose = 'other') {
191
+ if (!name) {
192
+ return false;
193
+ }
194
+ const normalized = normalizeFieldName(name);
195
+ if (SENSITIVE_FIELD_PATTERNS.some((pattern) => normalized.includes(pattern))) {
196
+ return true;
197
+ }
198
+ const wordStarts = wordStartOffsets(name);
199
+ if (SENSITIVE_FIELD_WORD_PATTERNS.some((pattern) => matchesAtWordStart(normalized, wordStarts, pattern))
200
+ || SENSITIVE_FIELD_WHOLE_WORD_PATTERNS.some((pattern) => matchesWholeWord(normalized, wordStarts, pattern))) {
201
+ return true;
202
+ }
203
+ if (purpose === 'duplicate-detection') {
204
+ return false;
205
+ }
206
+ return BIRTH_DATE_FIELD_PATTERNS.some((pattern) => normalized.includes(pattern))
207
+ || BIRTH_DATE_FIELD_WORD_PATTERNS.some((pattern) => matchesAtWordStart(normalized, wordStarts, pattern));
208
+ }
209
+ /** Drops every field that must not be embedded for this purpose, preserving order. */
210
+ export function WithoutSensitiveFields(fields, purpose = 'other') {
211
+ return fields.filter((f) => !IsSensitiveFieldName(f.Name, purpose));
212
+ }
213
+ /**
214
+ * Sensitive field names referenced by a template's `{{Placeholder}}` tokens.
215
+ *
216
+ * This is the second layer. The template is hand-editable after the model returns, so a
217
+ * user can type a field the picker never offered. Returns the offending names so the
218
+ * refusal can say which ones, rather than only that something was wrong.
219
+ */
220
+ export function SensitiveFieldsInTemplate(templateText, purpose = 'other') {
221
+ const found = new Set();
222
+ for (const token of TemplatePlaceholderNames(templateText)) {
223
+ if (IsSensitiveFieldName(token, purpose)) {
224
+ found.add(token);
225
+ }
226
+ }
227
+ return [...found].sort();
228
+ }
229
+ /**
230
+ * Field names referenced by a template's `{{Placeholder}}` tokens, in order of appearance
231
+ * and including repeats.
232
+ *
233
+ * Exported so the field-count cap in `vector-document-rules.ts` parses placeholders the same
234
+ * way this refusal does. Two parsers that drift apart would mean a template that satisfies
235
+ * one guard and evades the other.
236
+ */
237
+ export function TemplatePlaceholderNames(templateText) {
238
+ if (!templateText) {
239
+ return [];
240
+ }
241
+ // Nunjucks/Handlebars-style placeholders. The field name is the first identifier
242
+ // inside the braces; filters and whitespace after it are ignored.
243
+ const placeholder = /\{\{\s*([A-Za-z0-9_.\-]+)/g;
244
+ const names = [];
245
+ let match;
246
+ while ((match = placeholder.exec(templateText)) !== null) {
247
+ names.push(match[1]);
248
+ }
249
+ return names;
250
+ }
251
+ //# sourceMappingURL=sensitive-fields.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sensitive-fields.js","sourceRoot":"","sources":["../../../../src/AI/components/vectors/sensitive-fields.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Fields that must never be embedded into a vector index.\n *\n * Vectorizing a column copies its values into a third-party vector database, where they\n * live outside the entity permissions that normally guard them and are retrievable by\n * nearest-neighbour search rather than by an authorized query. For a password hash, a\n * national insurance or social security number, a bank account or a salary, that is not a\n * feature with a caveat — it is a disclosure.\n *\n * The field picker's suggestions come from a language model, which has no notion of which\n * columns are sensitive and will happily propose `PasswordHash` if the use case seems to\n * call for identity matching. So the refusal cannot live in the prompt: it has to be a\n * property of the code on both sides of the model.\n *\n * Two layers, deliberately:\n *\n * 1. The model never sees these fields, on the entity or on its related entities, so it\n * cannot suggest them.\n * 2. The saved template is re-checked, because the template is hand-editable after the\n * model returns and a user can type a field name the picker never offered.\n *\n * Layer 1 alone would be a prompt-shaped guarantee, which is no guarantee at all.\n */\n\n/**\n * Substrings that mark a column as unembeddable, matched case-insensitively against the\n * field name with separators ignored — so `password_hash`, `PasswordHash` and `PASSWORD`\n * all match the same rule.\n *\n * These are substrings rather than whole names on purpose. Real schemas spell the same\n * concept a dozen ways (`SSNumber`, `employee_ssn`, `ClientSecretEncrypted`), and the cost\n * of the two kinds of mistake is not symmetric: refusing a harmless column produces a\n * visible, correctable annoyance, while embedding a sensitive one is silent and, once\n * written to the index, not fully reversible.\n *\n * That asymmetry justifies over-refusing a column that RESEMBLES the concept — `AuthorName`\n * contains `auth`, and `auth` really is the prefix of authentication, so the resemblance\n * carries some signal and refusing it is a trade we accept. It does not justify a match\n * with no semantic relationship at all. `Rating` is not refused because it looks like a tax\n * identifier; it is refused because the letters `t-i-n` happen to sit inside an unrelated\n * English word, which trades away a usable column for no signal whatsoever. Patterns of\n * that shape belong in {@link SENSITIVE_FIELD_WORD_PATTERNS} or nowhere — see\n * `vector-sensitive-fields.test.ts`, which pins the refusal count over MJ's own field names\n * so that re-adding one is a red test rather than a quiet doubling of the blast radius.\n */\nconst SENSITIVE_FIELD_PATTERNS: ReadonlyArray<string> = [\n // Credentials and secrets. `token` is NOT here on its own: MJ alone has 36 columns that\n // are token *counts* (`ContextWindowMaxTokens`, `InputTokenLimit`, `FirstTokenTime`),\n // and a count is not a secret. The compounds below keep the credentials; `AuthToken`\n // and `OAuthToken` are still caught by `auth`.\n 'password', 'passwd', 'pwd', 'secret', 'apikey', 'accesskey', 'privatekey',\n 'accesstoken', 'refreshtoken', 'apitoken', 'bearertoken', 'sessiontoken',\n 'salt', 'hash', 'credential', 'auth', 'connectionstring', 'passcode', 'biometric',\n // Government and tax identifiers. `tin` is deliberately absent: `taxid` already covers\n // the concept, while `t-i-n` is a substring of ordinary English (Ra-tin-g, Marke-tin-g,\n // Set-tin-gs, Mee-tin-g) and matched 43 MJ columns, none of them a tax identifier.\n // A whole-word `tin` is in SENSITIVE_FIELD_WHOLE_WORD_PATTERNS below. `pin` and `otp`\n // are whole-word for the same reason; `passcode` and `biometric` are long enough to be\n // safe as substrings.\n 'socialsec', 'nationalinsurance', 'taxid', 'passport', 'driverlicense', 'driverslicense',\n // Financial\n 'bank', 'routing', 'iban', 'swift', 'accountnumber', 'cardnumber', 'cardnum', 'cvv',\n 'cvc', 'creditcard', 'salary', 'compensation', 'income',\n // Protected characteristics and other special-category data\n 'ethnic', 'religion', 'political', 'sexualorientation', 'disability',\n 'healthcondition', 'diagnosis', 'medicalrecord',\n];\n\n/**\n * Date of birth: refused like the patterns above, except in a document built for duplicate\n * detection. Telling two records for the same person apart from two people who share a name\n * is what a birth date is for, and without it duplicate detection on people loses most of its\n * precision. Every other purpose (search, classification) still refuses it.\n *\n * `dateofbirth` and `birthdate` match anywhere in the name; `dob` must begin a word, for the\n * reason given at {@link SENSITIVE_FIELD_WORD_PATTERNS}.\n */\nconst BIRTH_DATE_FIELD_PATTERNS: ReadonlyArray<string> = ['dateofbirth', 'birthdate'];\nconst BIRTH_DATE_FIELD_WORD_PATTERNS: ReadonlyArray<string> = ['dob'];\n\n/**\n * What the vectors are for. A `'duplicate-detection'` document may embed a date of birth (see\n * {@link BIRTH_DATE_FIELD_PATTERNS}); every other refusal applies to every purpose.\n */\nexport type VectorFieldPurpose = 'duplicate-detection' | 'other';\n\n/**\n * Patterns that must BEGIN A WORD in the field name rather than appear anywhere in it.\n *\n * These three are short enough that as bare substrings they collide with unrelated words\n * and catch nothing real: `ssn` is inside `cla-ssn-ame` (every `*ClassName` column in MJ),\n * `dob` is inside `recor-dob-ject`, `race` is inside `StackT-race` and `T-race-ID`. Each of\n * those matches carries zero information about sensitivity, and because the template check\n * throws, a user who needs `{{Rating}}` or `{{StackTrace}}` has no workaround at all — it\n * is a feature they cannot use, not an annoyance they route around.\n *\n * Anchoring loses no true positives, because all three are whole words wherever they are\n * meant: `ssn`, `employee_ssn`, `EmployeeSSN`, `SSNumber`, `dob`, `DOB`, `race`,\n * `EmployeeRace`. (`dob` itself lives in {@link BIRTH_DATE_FIELD_WORD_PATTERNS}, because duplicate\n * detection may use it.)\n *\n * `idtoken` is here for the same reason: as a substring it sits inside `Pa-idToken-s` and\n * `Val-idToken-Count`, which are counts, while `IdToken`, `IDToken` and `id_token` all begin\n * a word.\n */\nconst SENSITIVE_FIELD_WORD_PATTERNS: ReadonlyArray<string> = ['ssn', 'race', 'idtoken'];\n\n/**\n * Patterns that must be a WHOLE WORD: begin a word and end at the end of the name or where\n * the next word begins.\n *\n * `tin` (tax identification number) is too short even for a word start: `Tint`, `TinyInt`\n * and `Tinker` all begin with it. As a word of its own it means one thing: `TIN`,\n * `VendorTIN`, `vendor_tin`, `TINNumber`.\n *\n * `pin` (a personal identification number) and `otp` (a one-time password) are whole words\n * for the same reason: `Shipping`, `Mapping`, `Pinned` and `Hotpot` contain them. `UserPin`,\n * `PIN` and `OTPCode` are refused; `Pinned` and `PinnedAt` are not.\n */\nconst SENSITIVE_FIELD_WHOLE_WORD_PATTERNS: ReadonlyArray<string> = ['tin', 'pin', 'otp'];\n\n/** Characters dropped by normalisation. The two spellings must stay in step. */\nconst SEPARATORS_GLOBAL = /[\\s_\\-.]/g;\nconst SEPARATOR = /[\\s_\\-.]/;\nconst LOWER_OR_DIGIT = /[a-z0-9]/;\nconst UPPER = /[A-Z]/;\nconst LOWER = /[a-z]/;\n\n/** Normalises a field name for matching: lower-cased, separators and spaces removed. */\nfunction normalizeFieldName(name: string): string {\n return name.toLowerCase().replace(SEPARATORS_GLOBAL, '');\n}\n\n/**\n * The offsets in `normalizeFieldName(name)` at which a word begins.\n *\n * Normalisation *removes* separators rather than replacing them, so by the time a pattern is\n * matched there is no separator left to anchor a `\\b` against — `employee_ssn` and\n * `className` are both one unbroken run of letters (`employeessn`, `classname`). A regex word\n * boundary would therefore match at offset 0 and nowhere else, which is not the boundary we\n * need. So the word starts are computed from the ORIGINAL spelling, where the evidence still\n * exists, and carried as offsets into the normalised string.\n *\n * A word starts at the beginning, after a separator, and at the two camelCase transitions:\n * lower-or-digit followed by upper (`employee|SSN`), and the tail of an acronym run that\n * begins a capitalised word (`SS|Number`, so `SSNumber` still matches `ssn`).\n */\nfunction wordStartOffsets(name: string): ReadonlySet<number> {\n const starts = new Set<number>();\n let offset = 0;\n let startsWord = true;\n for (let i = 0; i < name.length; i++) {\n const ch = name[i];\n if (SEPARATOR.test(ch)) {\n // Dropped by normalizeFieldName, so it consumes no offset — but whatever\n // survives next begins a word.\n startsWord = true;\n continue;\n }\n const prev = i > 0 ? name[i - 1] : '';\n const next = i + 1 < name.length ? name[i + 1] : '';\n if (UPPER.test(ch) && (LOWER_OR_DIGIT.test(prev) || (UPPER.test(prev) && LOWER.test(next)))) {\n startsWord = true;\n }\n if (startsWord) {\n starts.add(offset);\n }\n startsWord = false;\n offset++;\n }\n return starts;\n}\n\n/** True when `pattern` occurs in `normalized` starting at one of `wordStarts`. */\nfunction matchesAtWordStart(normalized: string, wordStarts: ReadonlySet<number>, pattern: string): boolean {\n let at = normalized.indexOf(pattern);\n while (at !== -1) {\n if (wordStarts.has(at)) {\n return true;\n }\n at = normalized.indexOf(pattern, at + 1);\n }\n return false;\n}\n\n/** True when `pattern` occurs in `normalized` as a whole word, by the offsets in `wordStarts`. */\nfunction matchesWholeWord(normalized: string, wordStarts: ReadonlySet<number>, pattern: string): boolean {\n let at = normalized.indexOf(pattern);\n while (at !== -1) {\n const end = at + pattern.length;\n if (wordStarts.has(at) && (end === normalized.length || wordStarts.has(end))) {\n return true;\n }\n at = normalized.indexOf(pattern, at + 1);\n }\n return false;\n}\n\n/**\n * True when this field name must not be embedded.\n *\n * Note `hash` and `auth` are deliberately broad. A column called `AuthorName` normalises to\n * `authorname`, which contains `auth` — so it is refused. That is the trade accepted above:\n * a visible false refusal the user can work around beats a silent disclosure they cannot.\n */\nexport function IsSensitiveFieldName(name: string | null | undefined, purpose: VectorFieldPurpose = 'other'): boolean {\n if (!name) {\n return false;\n }\n const normalized = normalizeFieldName(name);\n if (SENSITIVE_FIELD_PATTERNS.some((pattern) => normalized.includes(pattern))) {\n return true;\n }\n const wordStarts = wordStartOffsets(name);\n if (SENSITIVE_FIELD_WORD_PATTERNS.some((pattern) => matchesAtWordStart(normalized, wordStarts, pattern))\n || SENSITIVE_FIELD_WHOLE_WORD_PATTERNS.some((pattern) => matchesWholeWord(normalized, wordStarts, pattern))) {\n return true;\n }\n if (purpose === 'duplicate-detection') {\n return false;\n }\n return BIRTH_DATE_FIELD_PATTERNS.some((pattern) => normalized.includes(pattern))\n || BIRTH_DATE_FIELD_WORD_PATTERNS.some((pattern) => matchesAtWordStart(normalized, wordStarts, pattern));\n}\n\n/** Drops every field that must not be embedded for this purpose, preserving order. */\nexport function WithoutSensitiveFields<T extends { Name: string }>(fields: ReadonlyArray<T>, purpose: VectorFieldPurpose = 'other'): T[] {\n return fields.filter((f) => !IsSensitiveFieldName(f.Name, purpose));\n}\n\n/**\n * Sensitive field names referenced by a template's `{{Placeholder}}` tokens.\n *\n * This is the second layer. The template is hand-editable after the model returns, so a\n * user can type a field the picker never offered. Returns the offending names so the\n * refusal can say which ones, rather than only that something was wrong.\n */\nexport function SensitiveFieldsInTemplate(templateText: string | null | undefined, purpose: VectorFieldPurpose = 'other'): string[] {\n const found = new Set<string>();\n for (const token of TemplatePlaceholderNames(templateText)) {\n if (IsSensitiveFieldName(token, purpose)) {\n found.add(token);\n }\n }\n return [...found].sort();\n}\n\n/**\n * Field names referenced by a template's `{{Placeholder}}` tokens, in order of appearance\n * and including repeats.\n *\n * Exported so the field-count cap in `vector-document-rules.ts` parses placeholders the same\n * way this refusal does. Two parsers that drift apart would mean a template that satisfies\n * one guard and evades the other.\n */\nexport function TemplatePlaceholderNames(templateText: string | null | undefined): string[] {\n if (!templateText) {\n return [];\n }\n // Nunjucks/Handlebars-style placeholders. The field name is the first identifier\n // inside the braces; filters and whitespace after it are ignored.\n const placeholder = /\\{\\{\\s*([A-Za-z0-9_.\\-]+)/g;\n const names: string[] = [];\n let match: RegExpExecArray | null;\n while ((match = placeholder.exec(templateText)) !== null) {\n names.push(match[1]);\n }\n return names;\n}\n"]}
@@ -0,0 +1,139 @@
1
+ /**
2
+ * Rules the Vectors tab applies when it turns a model suggestion into real metadata.
3
+ *
4
+ * Three separate refusals live here, and they share a theme with `sensitive-fields.ts`: the
5
+ * suggestion comes from a language model and the template stays hand-editable afterwards, so
6
+ * anything that matters has to be a property of the code rather than of the prompt.
7
+ *
8
+ * 1. The Entity Document Type is resolved from the use case the user actually picked,
9
+ * instead of always writing a duplicate-detection document.
10
+ * 2. The number of interpolated fields is bounded, because an unbounded list is how a
11
+ * template balloons and how columns nobody chose end up embedded.
12
+ * 3. MemberJunction's own internal entities are not offered as vectorization targets.
13
+ *
14
+ * Kept out of the component so each rule can be tested as a function rather than through a
15
+ * TestBed, matching how `sensitive-fields.ts` is structured.
16
+ */
17
+ /**
18
+ * The Entity Document Type each use case writes.
19
+ *
20
+ * The dialog offers these use cases, and until now every one of them produced a
21
+ * `Record Duplicate` document — the type name was hardcoded in the save path, so choosing
22
+ * "search" silently discarded the choice and wrote the wrong type. A vector pool is typed:
23
+ * `Provider.SearchEntity` and the Search Entity action read `Search`-typed documents, so a
24
+ * search request stored as `Record Duplicate` is not merely mislabelled, it is not found by
25
+ * the thing that wanted it.
26
+ *
27
+ * The two names here are MJ's own, not invented: both are seeded Entity Document Types, and
28
+ * the Vectorize Entity action's `EntityDocumentType` parameter is documented as defaulting
29
+ * to `Record Duplicate` and accepting `Search`.
30
+ *
31
+ * The dialog's Use Case buttons are exactly this map's keys. There is no `classification`
32
+ * entry: MJ seeds no `Classification` type, so that button failed on every stock install and
33
+ * was removed until the type exists (#5630).
34
+ */
35
+ export declare const ENTITY_DOCUMENT_TYPE_BY_USE_CASE: Readonly<Record<string, string>>;
36
+ /**
37
+ * The Entity Document Type name for a use case, or null when the use case is unrecognised.
38
+ *
39
+ * Returns null rather than falling back to a default: a use case this code does not know
40
+ * about must not quietly become a duplicate-detection document.
41
+ */
42
+ export declare function EntityDocumentTypeForUseCase(useCase: string | null | undefined): string | null;
43
+ /**
44
+ * The use case the suggestion prompt is given for an existing document of this type, or null
45
+ * when the type has none (e.g. `Context`).
46
+ *
47
+ * The edit panel's "Regenerate template" uses it, so a regenerated template still matches the
48
+ * document's type. It reads the same map as {@link EntityDocumentTypeForUseCase}, so the two
49
+ * directions cannot drift. Callers refuse on null rather than substitute a use case.
50
+ */
51
+ export declare function SuggestionUseCaseForDocumentType(typeName: string | null | undefined): string | null;
52
+ /**
53
+ * The most distinct fields one generated template may interpolate.
54
+ *
55
+ * Derived from the suggestion prompt's own contract rather than copied from elsewhere.
56
+ * `metadata/prompts/templates/system/entity-document-suggestion.template.md` requires
57
+ * "**Output must be 1-4 natural language sentences**" and explains at length why a
58
+ * key-value dump embeds badly compared with English prose.
59
+ *
60
+ * So: 4 sentences — the prompt's own ceiling — times 6 interpolations per sentence, past
61
+ * which a sentence stops being the natural English the prompt demands and becomes the dump
62
+ * it explicitly forbids. That gives 24.
63
+ *
64
+ * Cross-check against the prompt's three worked examples, which interpolate 8, 6 and 4
65
+ * distinct placeholders: 24 is three times the richest thing the prompt itself demonstrates,
66
+ * so the cap cannot fire on anything the prompt teaches a model to produce. It fires when a
67
+ * model has ignored the sentence contract and reached for the whole table.
68
+ */
69
+ export declare const MAX_TEMPLATE_FIELDS = 24;
70
+ /** Distinct `{{Placeholder}}` field names a template interpolates. */
71
+ export declare function TemplateFieldNames(templateText: string | null | undefined): string[];
72
+ /**
73
+ * Truncates a model-supplied field list to the cap.
74
+ *
75
+ * Truncation rather than refusal, because this list is the model's own description of what
76
+ * it selected and is shown as chips — dropping the tail and saying so is more useful than
77
+ * discarding the whole suggestion. The authoritative refusal is on the template, below,
78
+ * since the template is what actually gets embedded.
79
+ */
80
+ export declare function CapSelectedFields(fields: ReadonlyArray<string> | null | undefined): {
81
+ fields: string[];
82
+ dropped: number;
83
+ };
84
+ /**
85
+ * The refusal message when a template interpolates more fields than the cap allows, or null
86
+ * when it is within the cap.
87
+ *
88
+ * A template cannot be truncated without mangling its prose, so this one refuses rather than
89
+ * trimming — and the message carries both numbers, because "too many fields" without the
90
+ * count is not actionable. The template is editable in the dialog, so the user can act on it
91
+ * without losing the suggestion.
92
+ */
93
+ export declare function TemplateFieldCapRefusal(templateText: string | null | undefined): string | null;
94
+ /**
95
+ * Schemas whose entities are not offered in the picker.
96
+ *
97
+ * Follows the existing frontend precedent — `FRONTEND_BLOCKED_SCHEMAS` in
98
+ * `DatabaseDesigner/services/database-designer.engine.ts` and in
99
+ * `DatabaseDesigner/components/create-wizard/steps/step-relationships.component.ts`, both
100
+ * `new Set(['__mj'])` — rather than inventing a rule.
101
+ */
102
+ export declare const VECTOR_BLOCKED_SCHEMAS: ReadonlySet<string>;
103
+ /**
104
+ * True when an entity may be offered as a vectorization target.
105
+ *
106
+ * The picker previously listed every entity in metadata, so MemberJunction's own ~378
107
+ * internal entities — prompt runs, audit logs, record changes, credentials — were offered
108
+ * alongside the user's business tables, both as noise and as embedding targets.
109
+ *
110
+ * Two rules, and they currently pick out the same set: every `MJ:`-prefixed entity is in the
111
+ * `__mj` schema and vice versa (378 and 378, exactly coextensive, in the 5.46 baseline). The
112
+ * name-prefix rule is therefore defence in depth against an MJ entity registered into some
113
+ * other schema, not extra coverage today. Said plainly so nobody later reads it as load
114
+ * bearing and is surprised.
115
+ *
116
+ * Note MJ does seed two `Search`-typed documents over `__mj` entities (`AI Agents Search`,
117
+ * `AI Prompts Search`), so framework entities are not unvectorizable in principle. Those
118
+ * arrive through metadata sync, which is a different authoring channel with a different
119
+ * audience; this dialog is where a business user configures vectorization of their own data,
120
+ * and burying their tables under the framework's is the defect being fixed.
121
+ */
122
+ export declare function IsVectorizableEntity(entity: {
123
+ Name: string;
124
+ SchemaName?: string | null;
125
+ }): boolean;
126
+ /** Match thresholds an Entity Document stores, as fractions of 1. */
127
+ export interface DocumentMatchThresholds {
128
+ Potential: number;
129
+ Absolute: number;
130
+ }
131
+ /**
132
+ * The thresholds a model suggestion can put on the document it becomes. The suggestion panel
133
+ * shows the model's potential and absolute thresholds, and Save used to drop them, so the
134
+ * document kept the defaults the user never saw. Returns null when the suggestion's values are
135
+ * not usable as stored: each must be a number above 0 and at most 1 (the same fraction-of-1
136
+ * scale the document uses), and potential may not exceed absolute.
137
+ */
138
+ export declare function SuggestedMatchThresholds(potential: number | null | undefined, absolute: number | null | undefined): DocumentMatchThresholds | null;
139
+ //# sourceMappingURL=vector-document-rules.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vector-document-rules.d.ts","sourceRoot":"","sources":["../../../../src/AI/components/vectors/vector-document-rules.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAMH;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,gCAAgC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAG7E,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAK9F;AAED;;;;;;;GAOG;AACH,wBAAgB,gCAAgC,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAQnG;AAID;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,mBAAmB,KAAK,CAAC;AAEtC,sEAAsE;AACtE,wBAAgB,kBAAkB,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,EAAE,CAEpF;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC7B,MAAM,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG,IAAI,GAAG,SAAS,GACjD;IAAE,MAAM,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAYvC;AAED;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAW9F;AAID;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB,EAAE,WAAW,CAAC,MAAM,CAAqB,CAAC;AAE7E;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,oBAAoB,CAChC,MAAM,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GACrD,OAAO,CAKT;AAED,qEAAqE;AACrE,MAAM,WAAW,uBAAuB;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CACpC,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EACpC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GACpC,uBAAuB,GAAG,IAAI,CAOhC"}