@claritylabs/cl-sdk 3.2.2 → 3.2.4

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.js CHANGED
@@ -3918,7 +3918,7 @@ function mergeOperationalProfile(base, candidate, validNodeIds, validSpanIds) {
3918
3918
  ];
3919
3919
  return PolicyOperationalProfileSchema.parse({
3920
3920
  ...base,
3921
- documentType: candidate.documentType === "quote" ? "quote" : candidate.documentType === "policy" ? "policy" : base.documentType,
3921
+ documentType: candidate.documentType === "policy" ? "policy" : base.documentType,
3922
3922
  policyTypes: Array.isArray(candidate.policyTypes) && candidate.policyTypes.length > 0 ? candidate.policyTypes : base.policyTypes,
3923
3923
  policyNumber,
3924
3924
  namedInsured,
@@ -4163,6 +4163,7 @@ Rules:
4163
4163
  - Keep a coverage when it is a real operational coverage/benefit even if only one term needs cleanup.
4164
4164
  - When changing a term's semantic meaning, set kind to the corrected normalized term kind.
4165
4165
  - Do not add new coverage rows or new terms; this pass cleans the existing projection.
4166
+ - 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.
4166
4167
  - 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.
4167
4168
  - For each coverage decision, always include termDecisions. Use [] when no terms need cleanup.
4168
4169
  - Keep reasons concise and factual.
@@ -5575,14 +5576,36 @@ function operationalEvidenceScore(span) {
5575
5576
  if (/\$[\d,.]+|[0-9]{1,2}\/[0-9]{1,2}\/[0-9]{2,4}|[0-9]{1,2}\s+[A-Za-z]{3,9}\s+[0-9]{4}/.test(text)) score += 3;
5576
5577
  return score;
5577
5578
  }
5579
+ function spanTableId(span) {
5580
+ return span.table?.tableId ?? span.metadata?.tableId;
5581
+ }
5582
+ function isTableCellSpan(span) {
5583
+ return spanSourceUnit(span) === "table_cell";
5584
+ }
5585
+ function isOperationalEvidenceAnchor(span) {
5586
+ const sourceUnit3 = spanSourceUnit(span);
5587
+ if (sourceUnit3 === "page" || sourceUnit3 === "table_cell") return false;
5588
+ if (sourceUnit3 === "table" || sourceUnit3 === "table_row") return true;
5589
+ const text = cleanText([span.text, span.formNumber].filter(Boolean).join(" "), "");
5590
+ if (!text) return false;
5591
+ 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;
5592
+ 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;
5593
+ if (/\b(coverage part|forms? and endorsements?|attached at inception|endorsement\s+(?:no\.?|number|#)?\s*[A-Z0-9])\b/i.test(text)) return true;
5594
+ if (/\$[\d,.]+|[0-9]{1,2}\/[0-9]{1,2}\/[0-9]{2,4}|[0-9]{1,2}\s+[A-Za-z]{3,9}\s+[0-9]{4}/.test(text)) return true;
5595
+ return false;
5596
+ }
5578
5597
  function operationalProfileEvidence(sourceTree, sourceSpans) {
5579
5598
  const sorted = [...sourceSpans].sort(
5580
5599
  (left, right) => (spanPageStart(left) ?? Number.MAX_SAFE_INTEGER) - (spanPageStart(right) ?? Number.MAX_SAFE_INTEGER) || (left.location?.charStart ?? Number.MAX_SAFE_INTEGER) - (right.location?.charStart ?? Number.MAX_SAFE_INTEGER) || left.id.localeCompare(right.id)
5581
5600
  );
5582
5601
  const selected = /* @__PURE__ */ new Set();
5602
+ const selectedTableIds = /* @__PURE__ */ new Set();
5583
5603
  for (let index = 0; index < sorted.length; index += 1) {
5584
5604
  const score = operationalEvidenceScore(sorted[index]);
5585
5605
  if (score < 8) continue;
5606
+ if (!isOperationalEvidenceAnchor(sorted[index])) continue;
5607
+ const tableId2 = spanTableId(sorted[index]);
5608
+ if (tableId2 && !isTableCellSpan(sorted[index])) selectedTableIds.add(tableId2);
5586
5609
  const page = spanPageStart(sorted[index]);
5587
5610
  for (let offset = -2; offset <= 2; offset += 1) {
5588
5611
  const neighborIndex = index + offset;
@@ -5593,9 +5616,17 @@ function operationalProfileEvidence(sourceTree, sourceSpans) {
5593
5616
  selected.add(neighborIndex);
5594
5617
  }
5595
5618
  }
5619
+ if (selectedTableIds.size > 0) {
5620
+ sorted.forEach((span, index) => {
5621
+ const tableId2 = spanTableId(span);
5622
+ if (!tableId2 || !selectedTableIds.has(tableId2) || isTableCellSpan(span)) return;
5623
+ const text = cleanText(span.text, "");
5624
+ if (text && text.length <= 5e3) selected.add(index);
5625
+ });
5626
+ }
5596
5627
  const nodeIdsBySpanId = sourceNodeIdsBySpanId(sourceTree);
5597
5628
  const seenText = /* @__PURE__ */ new Set();
5598
- return [...selected].sort((left, right) => left - right).map((index) => sorted[index]).filter((span) => span.sourceUnit !== "table_cell").flatMap((span) => {
5629
+ const entries = [...selected].sort((left, right) => left - right).map((index) => sorted[index]).filter((span) => !isTableCellSpan(span)).flatMap((span) => {
5599
5630
  const text = cleanText(span.text, "");
5600
5631
  if (!text) return [];
5601
5632
  const key = `${spanPageStart(span) ?? "na"}:${text.toLowerCase().slice(0, 240)}`;
@@ -5610,7 +5641,10 @@ function operationalProfileEvidence(sourceTree, sourceSpans) {
5610
5641
  formNumber: span.formNumber,
5611
5642
  text: text.slice(0, span.sourceUnit === "page" ? 1200 : 900)
5612
5643
  }];
5613
- }).slice(0, 180);
5644
+ });
5645
+ const detailEntries = entries.filter((entry) => entry.sourceUnit !== "page");
5646
+ const pageEntries = entries.filter((entry) => entry.sourceUnit === "page");
5647
+ return (detailEntries.length >= 20 ? detailEntries : [...detailEntries, ...pageEntries]).slice(0, 180);
5614
5648
  }
5615
5649
  function sourceTreeRootId(sourceTree) {
5616
5650
  return sourceTree.find((node) => node.kind === "document")?.id;
@@ -5641,7 +5675,7 @@ function buildOperationalProfilePrompt(sourceTree, sourceSpans) {
5641
5675
  const fallbackNodes = evidence.length ? [] : operationalProfilePromptNodes(sourceTree).map(
5642
5676
  (node) => compactNode2(node, node.kind === "page" || node.kind === "endorsement" ? 900 : 700)
5643
5677
  );
5644
- return `Extract a source-backed operational profile for an insurance policy or quote.
5678
+ return `Extract a source-backed operational profile for an insurance policy.
5645
5679
 
5646
5680
  Return only high-value operational facts needed for policy lists, Q&A, compliance, and certificate generation:
5647
5681
  - policy number, named insured, insurer/carrier/security, broker/producer, policy period, retroactive date, premium
@@ -5657,6 +5691,7 @@ Rules:
5657
5691
  - On declarations pages, treat "Item N" labels as section boundaries. Use Item 6 or equivalent coverage-schedule rows for coverage limits, deductibles, aggregate terms, and retroactive dates; do not merge Item 7 premium, Item 8 ERP, Item 9 producer, or Item 10 forms into Item 6 coverage facts.
5658
5692
  - A coverage schedule row's coverage name should come from the "Coverage Part" or equivalent row label. Limit, deductible, aggregate, sublimit, retention, and retroactive-date values belong as nested terms under that coverage, not in the coverage title.
5659
5693
  - If a coverage schedule continues onto the next page before the next item marker, include the continuation rows in the same coverage or declaration item.
5694
+ - If one schedule row or continuation row states the same amount with multiple bases, such as "$1,000,000 Each Claim / Aggregate", return separate limit terms for each basis using the same value instead of one combined "Each Claim / Aggregate" term.
5660
5695
  - Forms-and-endorsements schedules are operational form inventory evidence, not coverage limits. Do not turn form schedule rows into coverage units unless the row also states a coverage-specific limit or deductible.
5661
5696
  - Keep each coverage unit tied to one evidence scope: a declaration/core schedule row, a core policy form section, or one specific endorsement schedule. Do not merge declaration facts and endorsement schedule facts into the same coverage unit, even when they use the same coverage name.
5662
5697
  - If the declarations schedule and an endorsement schedule both list Network Security, Social Engineering Fraud, Regulatory Proceedings, or another same-named coverage, return separate coverage units for each supported source scope.