@claritylabs/cl-sdk 3.2.4 → 3.2.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.
package/dist/index.mjs CHANGED
@@ -3576,6 +3576,16 @@ var OPERATIONAL_COVERAGE_TERM_KINDS2 = [
3576
3576
  "other"
3577
3577
  ];
3578
3578
  var OperationalCoverageTermKindSchema = z21.enum(OPERATIONAL_COVERAGE_TERM_KINDS2);
3579
+ var CleanupTermSchema = z21.object({
3580
+ kind: OperationalCoverageTermKindSchema,
3581
+ label: z21.string(),
3582
+ value: z21.string(),
3583
+ amount: z21.number().nullable().optional(),
3584
+ appliesTo: z21.string().nullable().optional(),
3585
+ sourceNodeIds: z21.array(z21.string()).optional(),
3586
+ sourceSpanIds: z21.array(z21.string()).optional(),
3587
+ reason: z21.string().optional()
3588
+ });
3579
3589
  var OperationalProfileCleanupSchema = z21.object({
3580
3590
  coverageDecisions: z21.array(z21.object({
3581
3591
  coverageIndex: z21.number().int().nonnegative(),
@@ -3599,7 +3609,8 @@ var OperationalProfileCleanupSchema = z21.object({
3599
3609
  appliesTo: z21.string().nullable().optional(),
3600
3610
  sourceNodeIds: z21.array(z21.string()).optional(),
3601
3611
  sourceSpanIds: z21.array(z21.string()).optional()
3602
- })).optional()
3612
+ })).optional(),
3613
+ termAdditions: z21.array(CleanupTermSchema).optional()
3603
3614
  })).default([]),
3604
3615
  warnings: z21.array(z21.string()).default([])
3605
3616
  });
@@ -3777,19 +3788,22 @@ Projection defects to look for:
3777
3788
  - Item references such as "shown in Item 7" or bare item numbers treated as money amounts.
3778
3789
  - Policy wording, exclusions, or unsupported prose copied into operational limit/deductible fields.
3779
3790
  - Header/value splits where "Limit of Liability", "Deductible", "Retroactive Date", "Aggregate", "Each Claim", or similar terms are attached to the wrong coverage row.
3791
+ - Collapsed combined bases where one candidate term says "Each Claim / Aggregate", "Each Loss / Aggregate", "Each Proceeding / Aggregate", or a continuation line supplies a separate policy aggregate, sub-limit, deductible, retention, retroactive date, or coinsurance term.
3780
3792
  - Repeated schedule headings projected as separate coverages when they only introduce the next coverage group.
3781
3793
 
3782
3794
  Rules:
3783
3795
  - Use internal reasoning, but return JSON decisions only.
3784
- - Do not invent policy facts. Keep, drop, or update only existing coverageIndex and termIndex entries.
3796
+ - Do not invent policy facts. Keep, drop, or update only existing coverageIndex and termIndex entries, except for source-backed termAdditions on an existing coverage.
3785
3797
  - Use sourceNodeIds and sourceSpanIds only from the provided source nodes or from the existing candidate entry.
3786
3798
  - Prefer dropping a malformed fact over speculative rewriting.
3787
3799
  - Keep a coverage when it is a real operational coverage/benefit even if only one term needs cleanup.
3788
3800
  - When changing a term's semantic meaning, set kind to the corrected normalized term kind.
3789
- - Do not add new coverage rows or new terms; this pass cleans the existing projection.
3790
- - If one existing term combines multiple real limit bases, such as "Each Claim / Aggregate", keep the combined term unless another existing term already represents the other basis. Do not relabel it to only one basis and lose information.
3801
+ - Do not add new coverage rows.
3802
+ - Add termAdditions only when a provided source node directly supports the missing term and the candidate already has the correct coverage row.
3803
+ - When replacing one combined term with split terms, drop the combined term and add one term per basis. For example, "$1,000,000 Each Claim / Aggregate" should become one each_claim_limit term and one aggregate_limit term, both with value "$1,000,000".
3804
+ - When a schedule continues onto the next page before the next item marker, attach continuation terms such as "Aggregate", "Policy Aggregate", coinsurance, deductible, or retroactive date to the previous coverage row.
3791
3805
  - Include every JSON key in each decision. Use null for scalar fields you are not changing and [] for source ID lists you are not changing.
3792
- - For each coverage decision, always include termDecisions. Use [] when no terms need cleanup.
3806
+ - For each coverage decision, always include termDecisions and termAdditions. Use [] when no existing terms need cleanup or no new terms are needed.
3793
3807
  - Keep reasons concise and factual.
3794
3808
 
3795
3809
  Candidate projection:
@@ -3930,9 +3944,50 @@ function applyTermCleanupDecision(term, decision, validNodeIds, validSpanIds) {
3930
3944
  }
3931
3945
  return next;
3932
3946
  }
3947
+ function termAdditionTouches(addition, predicate) {
3948
+ return predicate({
3949
+ kind: addition.kind,
3950
+ label: cleanProfileValue(addition.label) ?? "",
3951
+ value: cleanProfileValue(addition.value) ?? ""
3952
+ });
3953
+ }
3933
3954
  function termDecisionsTouch(coverage, decisions, predicate) {
3934
3955
  return decisions.filter((decision) => decision.action !== "keep").some((decision) => termDecisionTouches(coverage, decision, predicate));
3935
3956
  }
3957
+ function termAdditionsTouch(additions, predicate) {
3958
+ return additions.some((addition) => termAdditionTouches(addition, predicate));
3959
+ }
3960
+ function termKey(term) {
3961
+ return [
3962
+ term.kind,
3963
+ cleanProfileValue(term.label)?.toLowerCase(),
3964
+ cleanProfileValue(term.value)?.toLowerCase()
3965
+ ].join("|");
3966
+ }
3967
+ function applyTermAddition(addition, validNodeIds, validSpanIds) {
3968
+ const label = cleanProfileValue(addition.label);
3969
+ const value = cleanProfileValue(addition.value);
3970
+ if (!label || !value) return void 0;
3971
+ const sourceNodeIds = validIds(addition.sourceNodeIds, validNodeIds);
3972
+ const sourceSpanIds = validIds(addition.sourceSpanIds, validSpanIds);
3973
+ if (sourceNodeIds.length === 0 && sourceSpanIds.length === 0) return void 0;
3974
+ const term = {
3975
+ kind: addition.kind,
3976
+ label,
3977
+ value,
3978
+ sourceNodeIds,
3979
+ sourceSpanIds
3980
+ };
3981
+ if (typeof addition.amount === "number" && Number.isFinite(addition.amount)) {
3982
+ term.amount = addition.amount;
3983
+ } else if (term.kind !== "retroactive_date") {
3984
+ const amount = amountFromOperationalValue(value);
3985
+ if (amount !== void 0) term.amount = amount;
3986
+ }
3987
+ const appliesTo = cleanProfileValue(addition.appliesTo);
3988
+ if (appliesTo) term.appliesTo = appliesTo;
3989
+ return term;
3990
+ }
3936
3991
  function applyCoverageCleanupDecision(coverage, decision, validNodeIds, validSpanIds) {
3937
3992
  if (!decision || decision.action === "keep") return coverage;
3938
3993
  if (decision.action === "drop") return void 0;
@@ -3963,23 +4018,31 @@ function applyCoverageCleanupDecision(coverage, decision, validNodeIds, validSpa
3963
4018
  const termDecisions = (decision.termDecisions ?? []).filter((termDecision) => termDecision.termIndex < coverage.limits.length);
3964
4019
  const termDecisionByIndex = new Map(termDecisions.map((termDecision) => [termDecision.termIndex, termDecision]));
3965
4020
  next.limits = coverage.limits.map((term, index) => applyTermCleanupDecision(term, termDecisionByIndex.get(index), validNodeIds, validSpanIds)).filter((term) => Boolean(term));
3966
- if (termDecisions.length > 0) {
3967
- if (decision.limit == null && termDecisionsTouch(coverage, termDecisions, isLimitTerm)) {
4021
+ const termAdditions = (decision.termAdditions ?? []).map((addition) => applyTermAddition(addition, validNodeIds, validSpanIds)).filter((term) => Boolean(term));
4022
+ const existingTermKeys = new Set(next.limits.map(termKey));
4023
+ for (const term of termAdditions) {
4024
+ const key = termKey(term);
4025
+ if (existingTermKeys.has(key)) continue;
4026
+ next.limits.push(term);
4027
+ existingTermKeys.add(key);
4028
+ }
4029
+ if (termDecisions.length > 0 || termAdditions.length > 0) {
4030
+ if (decision.limit == null && (termDecisionsTouch(coverage, termDecisions, isLimitTerm) || termAdditionsTouch(termAdditions, isLimitTerm))) {
3968
4031
  const value = primaryLimitFromTerms(next.limits);
3969
4032
  if (value) next.limit = value;
3970
4033
  else delete next.limit;
3971
4034
  }
3972
- if (decision.deductible == null && termDecisionsTouch(coverage, termDecisions, isDeductibleTerm)) {
4035
+ if (decision.deductible == null && (termDecisionsTouch(coverage, termDecisions, isDeductibleTerm) || termAdditionsTouch(termAdditions, isDeductibleTerm))) {
3973
4036
  const value = deductibleFromTerms(next.limits);
3974
4037
  if (value) next.deductible = value;
3975
4038
  else delete next.deductible;
3976
4039
  }
3977
- if (decision.premium == null && termDecisionsTouch(coverage, termDecisions, isPremiumTerm)) {
4040
+ if (decision.premium == null && (termDecisionsTouch(coverage, termDecisions, isPremiumTerm) || termAdditionsTouch(termAdditions, isPremiumTerm))) {
3978
4041
  const value = premiumFromTerms(next.limits);
3979
4042
  if (value) next.premium = value;
3980
4043
  else delete next.premium;
3981
4044
  }
3982
- if (decision.retroactiveDate == null && termDecisionsTouch(coverage, termDecisions, isRetroactiveDateTerm)) {
4045
+ if (decision.retroactiveDate == null && (termDecisionsTouch(coverage, termDecisions, isRetroactiveDateTerm) || termAdditionsTouch(termAdditions, isRetroactiveDateTerm))) {
3983
4046
  const value = retroactiveDateFromTerms(next.limits);
3984
4047
  if (value) next.retroactiveDate = value;
3985
4048
  else delete next.retroactiveDate;
@@ -5208,10 +5271,13 @@ function isTableCellSpan(span) {
5208
5271
  }
5209
5272
  function isOperationalEvidenceAnchor(span) {
5210
5273
  const sourceUnit3 = spanSourceUnit(span);
5211
- if (sourceUnit3 === "page" || sourceUnit3 === "table_cell") return false;
5274
+ if (sourceUnit3 === "table_cell") return false;
5212
5275
  if (sourceUnit3 === "table" || sourceUnit3 === "table_row") return true;
5213
5276
  const text = cleanText([span.text, span.formNumber].filter(Boolean).join(" "), "");
5214
5277
  if (!text) return false;
5278
+ if (sourceUnit3 === "page") {
5279
+ return hasSubstantiveDeclarationsScheduleText(text) || /\b(declarations?|named insured|policy number|policy period|effective date|expiration date|premium|total due|producer)\b/i.test(text);
5280
+ }
5215
5281
  if (/\bitem\s+\d+\.?\s*(?:named insured|policy number|policy period|renewal|form of business|coverage parts?|premium|extended reporting|producer|forms? and endorsements?)\b/i.test(text)) return true;
5216
5282
  if (/\b(policy\s*(number|period)|effective date|expiration date|expiry date|named insured|insurer|carrier|broker|producer|premium|total due)\b/i.test(text)) return true;
5217
5283
  if (/\b(coverage part|forms? and endorsements?|attached at inception|endorsement\s+(?:no\.?|number|#)?\s*[A-Z0-9])\b/i.test(text)) return true;
@@ -5268,7 +5334,7 @@ function operationalProfileEvidence(sourceTree, sourceSpans) {
5268
5334
  });
5269
5335
  const detailEntries = entries.filter((entry) => entry.sourceUnit !== "page");
5270
5336
  const pageEntries = entries.filter((entry) => entry.sourceUnit === "page");
5271
- return (detailEntries.length >= 20 ? detailEntries : [...detailEntries, ...pageEntries]).slice(0, 180);
5337
+ return [...detailEntries, ...pageEntries].slice(0, 180);
5272
5338
  }
5273
5339
  function sourceTreeRootId(sourceTree) {
5274
5340
  return sourceTree.find((node) => node.kind === "document")?.id;