@claritylabs/cl-sdk 3.2.10 → 3.2.12

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
@@ -334,6 +334,10 @@ var PolicyTypeSchema = z2.enum([
334
334
  "watercraft",
335
335
  "recreational_vehicle",
336
336
  "farm_ranch",
337
+ "life",
338
+ "critical_illness",
339
+ "disability",
340
+ "long_term_care",
337
341
  "pet",
338
342
  "travel",
339
343
  "identity_theft",
@@ -2139,6 +2143,179 @@ var PolicyOperationalProfileSchema = z20.object({
2139
2143
  warnings: z20.array(z20.string()).default([])
2140
2144
  });
2141
2145
 
2146
+ // src/source/policy-types.ts
2147
+ var POLICY_TYPE_KEYS = new Set(POLICY_TYPES);
2148
+ var POLICY_TYPE_ALIASES = {
2149
+ "general liability": "general_liability",
2150
+ "commercial general liability": "general_liability",
2151
+ cgl: "general_liability",
2152
+ "commercial property": "commercial_property",
2153
+ property: "property",
2154
+ "property insurance": "commercial_property",
2155
+ "commercial auto": "commercial_auto",
2156
+ "commercial automobile": "commercial_auto",
2157
+ "business auto": "commercial_auto",
2158
+ "business automobile": "commercial_auto",
2159
+ "auto physical damage": "commercial_auto",
2160
+ "automobile physical damage": "commercial_auto",
2161
+ "hired non owned auto": "non_owned_auto",
2162
+ "hired non-owned auto": "non_owned_auto",
2163
+ "non owned auto": "non_owned_auto",
2164
+ "non-owned auto": "non_owned_auto",
2165
+ "workers comp": "workers_comp",
2166
+ "workers compensation": "workers_comp",
2167
+ "workers' compensation": "workers_comp",
2168
+ umbrella: "umbrella",
2169
+ "excess liability": "excess_liability",
2170
+ "professional liability": "professional_liability",
2171
+ "errors and omissions": "professional_liability",
2172
+ "e&o": "professional_liability",
2173
+ cyber: "cyber",
2174
+ "cyber liability": "cyber",
2175
+ "network security": "cyber",
2176
+ "privacy liability": "cyber",
2177
+ epli: "epli",
2178
+ "employment practices liability": "epli",
2179
+ "directors and officers": "directors_officers",
2180
+ "directors & officers": "directors_officers",
2181
+ "d&o": "directors_officers",
2182
+ "fiduciary liability insurance": "fiduciary_liability",
2183
+ "crime insurance": "crime_fidelity",
2184
+ "fidelity bond": "crime_fidelity",
2185
+ "inland marine insurance": "inland_marine",
2186
+ "motor truck cargo": "inland_marine",
2187
+ "motor truck cargo legal liability": "inland_marine",
2188
+ "builders risk insurance": "builders_risk",
2189
+ "pollution liability": "environmental",
2190
+ "premises pollution liability": "environmental",
2191
+ "environmental liability": "environmental",
2192
+ "ocean marine insurance": "ocean_marine",
2193
+ "surety bond": "surety",
2194
+ "product liability insurance": "product_liability",
2195
+ "life insurance": "life",
2196
+ "permanent life": "life",
2197
+ "term life": "life",
2198
+ "whole life": "life",
2199
+ "universal life": "life",
2200
+ "critical illness": "critical_illness",
2201
+ "critical illness insurance": "critical_illness",
2202
+ "disability insurance": "disability",
2203
+ "long term care": "long_term_care",
2204
+ "long-term care": "long_term_care"
2205
+ };
2206
+ var POLICY_TYPE_TEXT_PATTERNS = [
2207
+ { type: "general_liability", pattern: /\b(?:commercial\s+)?general\s+liability\b|\bcgl\b/i },
2208
+ { type: "commercial_property", pattern: /\bcommercial\s+property\b|\bproperty\s+insurance\b/i },
2209
+ { type: "commercial_auto", pattern: /\bcommercial\s+auto(?:mobile)?\b|\bbusiness\s+auto(?:mobile)?\b|\bauto(?:mobile)?\s+physical\s+damage\b/i },
2210
+ { type: "non_owned_auto", pattern: /\b(?:hired\s+(?:and\s+)?)?non[-\s]?owned\s+auto\b/i },
2211
+ { type: "workers_comp", pattern: /\bworkers['’]?\s+comp(?:ensation)?\b/i },
2212
+ { type: "umbrella", pattern: /\bcommercial\s+umbrella\b|\bumbrella\s+liability\b/i },
2213
+ { type: "excess_liability", pattern: /\bexcess\s+liability\b/i },
2214
+ { type: "professional_liability", pattern: /\bprofessional\s+liability\b|\berrors?\s*(?:and|&)\s*omissions?\b|\be&o\b/i },
2215
+ { type: "cyber", pattern: /\bcyber\b|\bnetwork\s+security\b|\bprivacy\s+liability\b/i },
2216
+ { type: "epli", pattern: /\bemployment\s+practices?\s+liability\b|\bepli\b/i },
2217
+ { type: "directors_officers", pattern: /\bdirectors?\s*(?:and|&)\s*officers?\b|\bd&o\b/i },
2218
+ { type: "fiduciary_liability", pattern: /\bfiduciary\s+liability\b/i },
2219
+ { type: "crime_fidelity", pattern: /\bcrime\b|\bfidelity\b/i },
2220
+ { type: "inland_marine", pattern: /\binland\s+marine\b|\bmotor\s+truck\s+cargo\b|\bcargo\s+legal\s+liability\b/i },
2221
+ { type: "builders_risk", pattern: /\bbuilders?\s+risk\b/i },
2222
+ { type: "environmental", pattern: /\bpollution\s+liability\b|\benvironmental\s+liability\b/i },
2223
+ { type: "ocean_marine", pattern: /\bocean\s+marine\b/i },
2224
+ { type: "surety", pattern: /\bsurety\b/i },
2225
+ { type: "product_liability", pattern: /\bproduct\s+liability\b|\bproducts?\s+completed\s+operations\b/i },
2226
+ { type: "bop", pattern: /\bbusiness\s*owners?\s+policy\b|\bbop\b/i },
2227
+ { type: "homeowners_ho3", pattern: /\bhomeowners?\s*(?:ho[-\s]?3)?\b/i },
2228
+ { type: "homeowners_ho5", pattern: /\bho[-\s]?5\b/i },
2229
+ { type: "renters_ho4", pattern: /\brenters?\b|\bho[-\s]?4\b/i },
2230
+ { type: "condo_ho6", pattern: /\bcondo(?:minium)?\b|\bho[-\s]?6\b/i },
2231
+ { type: "dwelling_fire", pattern: /\bdwelling\s+fire\b/i },
2232
+ { type: "personal_auto", pattern: /\bpersonal\s+auto\b/i },
2233
+ { type: "personal_umbrella", pattern: /\bpersonal\s+umbrella\b/i },
2234
+ { type: "flood_private", pattern: /\bflood\b/i },
2235
+ { type: "earthquake", pattern: /\bearthquake\b/i },
2236
+ { type: "personal_inland_marine", pattern: /\bpersonal\s+(?:articles|inland\s+marine)\b/i },
2237
+ { type: "watercraft", pattern: /\bwatercraft\b|\bboat\s+insurance\b/i },
2238
+ { type: "recreational_vehicle", pattern: /\brecreational\s+vehicle\b|\brv\s+insurance\b/i },
2239
+ { type: "farm_ranch", pattern: /\bfarm\b|\branch\b/i },
2240
+ { type: "life", pattern: /\blife\s+insurance\b|\bterm\s+life\b|\bwhole\s+life\b|\buniversal\s+life\b/i },
2241
+ { type: "critical_illness", pattern: /\bcritical\s+illness\b/i },
2242
+ { type: "disability", pattern: /\bdisability\s+insurance\b|\btotal\s+disability\b/i },
2243
+ { type: "long_term_care", pattern: /\blong[-\s]?term\s+care\b/i },
2244
+ { type: "pet", pattern: /\bpet\s+insurance\b/i },
2245
+ { type: "travel", pattern: /\btravel\s+insurance\b/i },
2246
+ { type: "identity_theft", pattern: /\bidentity\s+theft\b/i },
2247
+ { type: "title", pattern: /\btitle\s+insurance\b/i }
2248
+ ];
2249
+ var POLICY_TYPES_FROM_COVERAGES_WARNING = "Policy types augmented from extracted coverage labels.";
2250
+ function normalizeWhitespace(value) {
2251
+ return value.replace(/\s+/g, " ").trim();
2252
+ }
2253
+ function normalizeOperationalPolicyTypes(values) {
2254
+ const types = Array.isArray(values) ? values.filter((value) => typeof value === "string") : [];
2255
+ const controlled = types.map((type) => type.trim().toLowerCase().replace(/\s+/g, " ")).map((type) => POLICY_TYPE_ALIASES[type] ?? type.replace(/[\s-]+/g, "_")).filter((type) => POLICY_TYPE_KEYS.has(type));
2256
+ const unique = [...new Set(controlled)].slice(0, 6);
2257
+ return unique.length ? unique : ["other"];
2258
+ }
2259
+ function hasSpecificPolicyType(types) {
2260
+ return types.some((type) => type !== "other");
2261
+ }
2262
+ function policyTypesFromText(value) {
2263
+ const text = normalizeWhitespace(value ?? "");
2264
+ if (!text) return [];
2265
+ const aliasType = POLICY_TYPE_ALIASES[text.toLowerCase()];
2266
+ if (aliasType && POLICY_TYPE_KEYS.has(aliasType)) return [aliasType];
2267
+ return POLICY_TYPE_TEXT_PATTERNS.filter(({ pattern }) => pattern.test(text)).map(({ type }) => type).filter((type) => POLICY_TYPE_KEYS.has(type));
2268
+ }
2269
+ function inferPolicyTypesFromOperationalCoverages(coverages) {
2270
+ const inferred = [];
2271
+ for (const coverage of coverages) {
2272
+ const limits = coverage.limits ?? [];
2273
+ const text = [
2274
+ coverage.coverageCode,
2275
+ coverage.formNumber,
2276
+ coverage.name,
2277
+ ...limits.flatMap((term) => [term.appliesTo, term.label])
2278
+ ].filter((value) => typeof value === "string" && value.trim().length > 0);
2279
+ for (const value of text) {
2280
+ for (const type of policyTypesFromText(value)) {
2281
+ if (!inferred.includes(type)) inferred.push(type);
2282
+ }
2283
+ }
2284
+ }
2285
+ return inferred.slice(0, 6);
2286
+ }
2287
+ function mergePolicyTypes(base, additions) {
2288
+ const merged = [
2289
+ ...base.filter((type) => type !== "other"),
2290
+ ...additions.filter((type) => type !== "other")
2291
+ ];
2292
+ const unique = [...new Set(merged)].slice(0, 6);
2293
+ return unique.length ? unique : base;
2294
+ }
2295
+ function resolveOperationalProfilePolicyTypes(params) {
2296
+ const inferred = inferPolicyTypesFromOperationalCoverages(params.coverages ?? []);
2297
+ const controlled = normalizeOperationalPolicyTypes(params.profileTypes);
2298
+ if (hasSpecificPolicyType(controlled)) {
2299
+ const policyTypes = mergePolicyTypes(controlled, inferred);
2300
+ return {
2301
+ policyTypes,
2302
+ source: policyTypes.length > controlled.filter((type) => type !== "other").length ? "profile_augmented" : "profile"
2303
+ };
2304
+ }
2305
+ const existingControlled = normalizeOperationalPolicyTypes(params.existingTypes);
2306
+ if (hasSpecificPolicyType(existingControlled)) {
2307
+ const policyTypes = mergePolicyTypes(existingControlled, inferred);
2308
+ return {
2309
+ policyTypes,
2310
+ source: policyTypes.length > existingControlled.filter((type) => type !== "other").length ? "existing_augmented" : "existing"
2311
+ };
2312
+ }
2313
+ if (inferred.length > 0) {
2314
+ return { policyTypes: inferred, source: "inferred" };
2315
+ }
2316
+ return { policyTypes: controlled, source: "profile" };
2317
+ }
2318
+
2142
2319
  // src/source/ids.ts
2143
2320
  function normalizeText(text) {
2144
2321
  return text.replace(/\s+/g, " ").trim();
@@ -2205,14 +2382,14 @@ function orderSourceEvidence(evidence) {
2205
2382
  }
2206
2383
 
2207
2384
  // src/source/extraction.ts
2208
- function normalizeWhitespace(value) {
2385
+ function normalizeWhitespace2(value) {
2209
2386
  return value.replace(/\s+/g, " ").trim();
2210
2387
  }
2211
2388
  function sanitizeIdPart(value) {
2212
2389
  return value.replace(/[^a-zA-Z0-9_.:-]/g, "_");
2213
2390
  }
2214
2391
  function buildSourceSpan(input, localIndex = 0) {
2215
- const text = normalizeWhitespace(input.text);
2392
+ const text = normalizeWhitespace2(input.text);
2216
2393
  const textHash = sourceSpanTextHash(text);
2217
2394
  const pagePart = input.pageStart ?? "na";
2218
2395
  const id = [
@@ -2247,7 +2424,7 @@ function buildSourceSpan(input, localIndex = 0) {
2247
2424
  });
2248
2425
  }
2249
2426
  function buildPageSourceSpans(pages) {
2250
- return pages.filter((page) => normalizeWhitespace(page.text).length > 0).map(
2427
+ return pages.filter((page) => normalizeWhitespace2(page.text).length > 0).map(
2251
2428
  (page, index) => buildSourceSpan(
2252
2429
  {
2253
2430
  documentId: page.documentId,
@@ -2298,7 +2475,7 @@ function buildSectionSourceSpans(pages, options = {}) {
2298
2475
  function buildTextSourceSpans(input, options = {}) {
2299
2476
  const maxChars = options.maxChars ?? 4e3;
2300
2477
  const overlapChars = Math.min(options.overlapChars ?? 0, Math.max(0, maxChars - 1));
2301
- const text = normalizeWhitespace(input.text);
2478
+ const text = normalizeWhitespace2(input.text);
2302
2479
  if (!text) return [];
2303
2480
  const spans = [];
2304
2481
  let cursor = 0;
@@ -2373,7 +2550,7 @@ function spanPage(span) {
2373
2550
  }
2374
2551
  function normalizeSourceSpanText(span) {
2375
2552
  const unit = sourceUnit(span);
2376
- const text = normalizeWhitespace(span.text);
2553
+ const text = normalizeWhitespace2(span.text);
2377
2554
  if (!text) return void 0;
2378
2555
  if (isDiscardableBoilerplate(text, unit)) return void 0;
2379
2556
  const cleanedText = cleanBoilerplateLines(text);
@@ -2385,7 +2562,7 @@ function normalizeSourceSpanText(span) {
2385
2562
  });
2386
2563
  }
2387
2564
  function isDiscardableBoilerplate(text, unit) {
2388
- const cleaned = normalizeWhitespace(text.replace(/\bColumn\s+\d+:\s*/gi, ""));
2565
+ const cleaned = normalizeWhitespace2(text.replace(/\bColumn\s+\d+:\s*/gi, ""));
2389
2566
  if (/^SPECIMEN POLICY\s+[-—]\s+FOR TESTING ONLY$/i.test(cleaned)) return true;
2390
2567
  if (/^Page\s+\d+\s+of\s+\d+$/i.test(cleaned)) return true;
2391
2568
  if (/^[A-Z]{2,}(?:-[A-Z0-9]{2,})+\s+\d{2}\s+\d{2}$/i.test(cleaned)) return true;
@@ -2394,24 +2571,24 @@ function isDiscardableBoilerplate(text, unit) {
2394
2571
  return false;
2395
2572
  }
2396
2573
  function isBoilerplateLine(line) {
2397
- const cleaned = normalizeWhitespace(line.replace(/\bColumn\s+\d+:\s*/gi, ""));
2574
+ const cleaned = normalizeWhitespace2(line.replace(/\bColumn\s+\d+:\s*/gi, ""));
2398
2575
  return isDiscardableBoilerplate(cleaned) || /^THIS IS A CLAIMS-MADE AND REPORTED POLICY\.? PLEASE READ IT CAREFULLY\.?$/i.test(cleaned);
2399
2576
  }
2400
2577
  function removedBoilerplateLines(text) {
2401
- return text.split(/\s{2,}|\r?\n/).map(normalizeWhitespace).filter((line) => line && isBoilerplateLine(line));
2578
+ return text.split(/\s{2,}|\r?\n/).map(normalizeWhitespace2).filter((line) => line && isBoilerplateLine(line));
2402
2579
  }
2403
2580
  function cleanBoilerplateLines(text) {
2404
2581
  const withoutInlineBoilerplate = text.replace(/\b(?:Column\s+\d+:\s*)?[A-Z]{2,}(?:-[A-Z0-9]{2,})+\s+\d{2}\s+\d{2}\s+(?:\|\s*)?(?:Column\s+\d+:\s*)?Page\s+\d+\s+of\s+\d+\b/gi, " ").replace(/\bSPECIMEN POLICY\s+[-—]\s+FOR TESTING ONLY\b/gi, " ").replace(/\bPage\s+\d+\s+of\s+\d+\b/gi, " ");
2405
2582
  const lines = withoutInlineBoilerplate.split(/\r?\n/);
2406
- const filtered = lines.map(normalizeWhitespace).filter((line) => line && !isBoilerplateLine(line));
2407
- return normalizeWhitespace(filtered.join(" "));
2583
+ const filtered = lines.map(normalizeWhitespace2).filter((line) => line && !isBoilerplateLine(line));
2584
+ return normalizeWhitespace2(filtered.join(" "));
2408
2585
  }
2409
2586
  function shouldMergeTextSpan(left, right) {
2410
2587
  if (sourceUnit(left) !== "text" || sourceUnit(right) !== "text") return false;
2411
2588
  if (spanPage(left) !== spanPage(right)) return false;
2412
2589
  if (left.metadata?.elementType === "title" || right.metadata?.elementType === "title") return false;
2413
- const leftText = normalizeWhitespace(left.text);
2414
- const rightText = normalizeWhitespace(right.text);
2590
+ const leftText = normalizeWhitespace2(left.text);
2591
+ const rightText = normalizeWhitespace2(right.text);
2415
2592
  if (!leftText || !rightText) return false;
2416
2593
  if (/[:.;!?)]$/.test(leftText)) return false;
2417
2594
  if (/^(?:[A-Z][A-Z0-9 &/(),.-]{8,}|Item\s+\d+|Section\s+\d+|Part\s+[A-Z]\b)/.test(rightText)) return false;
@@ -2432,7 +2609,7 @@ function mergeTextRuns(spans) {
2432
2609
  return result;
2433
2610
  }
2434
2611
  function mergeTextSpanPair(left, right) {
2435
- const text = normalizeWhitespace(`${left.text} ${right.text}`);
2612
+ const text = normalizeWhitespace2(`${left.text} ${right.text}`);
2436
2613
  const merged = retextSpan(left, text, {
2437
2614
  mergedSourceSpanIds: [left.metadata?.mergedSourceSpanIds, left.id, right.id, right.metadata?.mergedSourceSpanIds].filter(Boolean).join(","),
2438
2615
  sourceSpanNormalization: "merged_text_run"
@@ -2484,7 +2661,7 @@ function splitPageIntoSections(text, headingPattern, minSectionChars) {
2484
2661
  if (current) sections.push(current);
2485
2662
  const suffix = match[1]?.trim();
2486
2663
  current = {
2487
- title: normalizeWhitespace(suffix ? `${line}` : line).slice(0, 120),
2664
+ title: normalizeWhitespace2(suffix ? `${line}` : line).slice(0, 120),
2488
2665
  lines: [line]
2489
2666
  };
2490
2667
  continue;
@@ -2494,7 +2671,7 @@ function splitPageIntoSections(text, headingPattern, minSectionChars) {
2494
2671
  if (current) sections.push(current);
2495
2672
  return sections.map((section) => ({
2496
2673
  title: section.title,
2497
- text: normalizeWhitespace(section.lines.join("\n"))
2674
+ text: normalizeWhitespace2(section.lines.join("\n"))
2498
2675
  })).filter((section) => section.text.length >= minSectionChars);
2499
2676
  }
2500
2677
  function inferFormNumber(text) {
@@ -2595,14 +2772,14 @@ function matchesFilters(span, filters) {
2595
2772
  }
2596
2773
 
2597
2774
  // src/source/tree.ts
2598
- function normalizeWhitespace2(value) {
2775
+ function normalizeWhitespace3(value) {
2599
2776
  return value.replace(/\s+/g, " ").trim();
2600
2777
  }
2601
2778
  function sanitizeIdPart2(value) {
2602
2779
  return value.replace(/[^a-zA-Z0-9_.:-]/g, "_");
2603
2780
  }
2604
2781
  function truncate(value, maxChars) {
2605
- const text = normalizeWhitespace2(value);
2782
+ const text = normalizeWhitespace3(value);
2606
2783
  return text.length > maxChars ? `${text.slice(0, maxChars).trimEnd()}...` : text;
2607
2784
  }
2608
2785
  function pageStart(span) {
@@ -2696,7 +2873,7 @@ function isTitleContentNode(node) {
2696
2873
  if (node.kind !== "text") return false;
2697
2874
  const isMarkedTitle = metadataString(node.metadata, "elementType") === "title" || metadataString(node.metadata, "sourceUnit") === "title";
2698
2875
  if (!isMarkedTitle) return false;
2699
- const text = normalizeWhitespace2(node.textExcerpt ?? node.title);
2876
+ const text = normalizeWhitespace3(node.textExcerpt ?? node.title);
2700
2877
  if (!text || text.length > 140) return false;
2701
2878
  const words = text.split(/\s+/);
2702
2879
  if (words.length > 14) return false;
@@ -3371,12 +3548,12 @@ function shouldFailQualityGate(mode, status) {
3371
3548
  import { z as z22 } from "zod";
3372
3549
 
3373
3550
  // src/source/operational-profile.ts
3374
- function normalizeWhitespace3(value) {
3551
+ function normalizeWhitespace4(value) {
3375
3552
  return value.replace(/\s+/g, " ").trim();
3376
3553
  }
3377
3554
  function cleanValue(value) {
3378
3555
  if (!value) return void 0;
3379
- return normalizeWhitespace3(value.replace(/^[\s:;#-]+|[\s;,.]+$/g, ""));
3556
+ return normalizeWhitespace4(value.replace(/^[\s:;#-]+|[\s;,.]+$/g, ""));
3380
3557
  }
3381
3558
  var OPERATIONAL_COVERAGE_TERM_KINDS = /* @__PURE__ */ new Set([
3382
3559
  "each_claim_limit",
@@ -3458,6 +3635,7 @@ function mergeOperationalProfile(base, candidate, validNodeIds, validSpanIds) {
3458
3635
  ...keepIds(record.sourceSpanIds, validSpanIds),
3459
3636
  ...limits.flatMap((term) => term.sourceSpanIds)
3460
3637
  ])];
3638
+ if (!name || sourceNodeIds2.length === 0 && sourceSpanIds2.length === 0) return void 0;
3461
3639
  return {
3462
3640
  name,
3463
3641
  coverageCode: typeof record.coverageCode === "string" ? cleanValue(record.coverageCode) : void 0,
@@ -3472,7 +3650,7 @@ function mergeOperationalProfile(base, candidate, validNodeIds, validSpanIds) {
3472
3650
  sourceNodeIds: sourceNodeIds2,
3473
3651
  sourceSpanIds: sourceSpanIds2
3474
3652
  };
3475
- }).filter((coverage) => coverage.name && (coverage.sourceNodeIds.length > 0 || coverage.sourceSpanIds.length > 0)) : base.coverages;
3653
+ }).filter((coverage) => Boolean(coverage)) : base.coverages;
3476
3654
  const sourceBackedParty = (role, value) => value ? {
3477
3655
  role,
3478
3656
  name: value.normalizedValue ?? value.value,
@@ -3540,10 +3718,18 @@ function mergeOperationalProfile(base, candidate, validNodeIds, validSpanIds) {
3540
3718
  ...base.warnings,
3541
3719
  ...Array.isArray(candidate.warnings) ? candidate.warnings.filter((warning) => typeof warning === "string" && warning.trim().length > 0) : []
3542
3720
  ];
3721
+ const resolvedPolicyTypes = resolveOperationalProfilePolicyTypes({
3722
+ profileTypes: candidate.policyTypes,
3723
+ existingTypes: base.policyTypes,
3724
+ coverages
3725
+ });
3726
+ if (resolvedPolicyTypes.source === "profile_augmented" || resolvedPolicyTypes.source === "existing_augmented" || resolvedPolicyTypes.source === "inferred") {
3727
+ warnings.push(POLICY_TYPES_FROM_COVERAGES_WARNING);
3728
+ }
3543
3729
  return PolicyOperationalProfileSchema.parse({
3544
3730
  ...base,
3545
3731
  documentType: candidate.documentType === "policy" ? "policy" : base.documentType,
3546
- policyTypes: Array.isArray(candidate.policyTypes) && candidate.policyTypes.length > 0 ? candidate.policyTypes : base.policyTypes,
3732
+ policyTypes: resolvedPolicyTypes.policyTypes,
3547
3733
  policyNumber,
3548
3734
  namedInsured,
3549
3735
  insurer,
@@ -3773,9 +3959,11 @@ Task:
3773
3959
  Projection defects to look for:
3774
3960
  - Generic labels such as "Column 3" that should be renamed from nearby row/header evidence.
3775
3961
  - Declaration or section headers projected as coverage names when the row evidence is actually a specific coverage, sub-limit, deductible, retention, retroactive date, or premium.
3962
+ - Premium-only, tax-only, fee-only, rating, exposure, reporting-value, or payment-plan rows projected as coverage rows.
3776
3963
  - Dangling continuation punctuation such as a trailing "/" copied into values.
3777
3964
  - Item references such as "shown in Item 7" or bare item numbers treated as money amounts.
3778
3965
  - Policy wording, exclusions, or unsupported prose copied into operational limit/deductible fields.
3966
+ - Premium, MGA Fee, taxes, stamping fees, total premium, total due, reporting values, or exposure annual rate used as a coverage limit.
3779
3967
  - Header/value splits where "Limit of Liability", "Deductible", "Retroactive Date", "Aggregate", "Each Claim", or similar terms are attached to the wrong coverage row.
3780
3968
  - Repeated schedule headings projected as separate coverages when they only introduce the next coverage group.
3781
3969
 
@@ -3785,7 +3973,8 @@ Rules:
3785
3973
  - Use sourceNodeIds and sourceSpanIds only from the provided source nodes or from the existing candidate entry.
3786
3974
  - Prefer dropping a malformed fact over speculative rewriting.
3787
3975
  - Keep a coverage when it is a real operational coverage/benefit even if only one term needs cleanup.
3788
- - Never drop a declaration or schedule coverage row that names a coverage and states policy-specific amounts, dates, deductibles, retentions, premiums, or coverage terms. Repair its terms instead.
3976
+ - Drop a coverage row when its only facts are premium, tax, fee, rating, reporting-value, exposure, or payment-plan facts and it has no source-backed limit, deductible, retention, retroactive date, sublimit, or benefit term.
3977
+ - Never drop a declaration or schedule coverage row that names a coverage and states policy-specific limits, dates, deductibles, retentions, sublimits, or benefit terms. Repair its terms instead.
3789
3978
  - When changing a term's semantic meaning, set kind to the corrected normalized term kind.
3790
3979
  - Do not add new coverage rows or new terms; this pass cleans the existing projection.
3791
3980
  - 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.
@@ -5126,8 +5315,10 @@ Rules:
5126
5315
  - When citing an evidence entry, copy its sourceSpanId into the returned sourceSpanIds array.
5127
5316
  - If a value is not directly supported, omit it.
5128
5317
  - Prefer declarations, schedules, premium tables, and endorsement schedules over generic policy wording.
5318
+ - For effective, expiration, retroactive, and other date fields, return a normalized YYYY-MM-DD value when the source date is unambiguous, including month-name dates such as "20 Feb 2026". Do not emit fragmented date text such as "20 2 2026".
5129
5319
  - For broker/producer, extract the agency or company legal name, not the license role, credential, or type. In a block like "Bayshore Insurance Brokers, LLC" followed by "Surplus Lines Broker - CA License No. ...", broker.value must be "Bayshore Insurance Brokers, LLC"; the surplus-lines role and license number are not the broker name.
5130
5320
  - 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.
5321
+ - Premium, tax, fee, payment-plan, rating, exposure, and reporting-value schedules are billing evidence, not coverage schedules. Extract the total policy premium into premium when supported, but do not create coverages[] entries from premium-only or fee-only rows, and never use Total Premium, MGA Fee, tax, stamping fee, reporting values, or exposure annual rate as a coverage limit.
5131
5322
  - 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.
5132
5323
  - 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.
5133
5324
  - 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.
@@ -5177,7 +5368,10 @@ var NORMALIZED_COMPATIBILITY_FIELDS = /* @__PURE__ */ new Set([
5177
5368
  "policyNumber",
5178
5369
  "namedInsured",
5179
5370
  "insurer",
5180
- "broker"
5371
+ "broker",
5372
+ "effectiveDate",
5373
+ "expirationDate",
5374
+ "retroactiveDate"
5181
5375
  ]);
5182
5376
  function valueOf(profile, key) {
5183
5377
  const value = profile[key];
@@ -12612,6 +12806,7 @@ export {
12612
12806
  POLICY_SECTION_TYPES,
12613
12807
  POLICY_TERM_TYPES,
12614
12808
  POLICY_TYPES,
12809
+ POLICY_TYPES_FROM_COVERAGES_WARNING,
12615
12810
  ParsedAnswerSchema,
12616
12811
  PaymentInstallmentSchema,
12617
12812
  PaymentPlanSchema,
@@ -12767,6 +12962,7 @@ export {
12767
12962
  getNextApplicationQuestions,
12768
12963
  getPdfPageCount,
12769
12964
  getTemplate,
12965
+ inferPolicyTypesFromOperationalCoverages,
12770
12966
  isDoclingExtractionInput,
12771
12967
  isFileReference,
12772
12968
  mergeQuestionAnswers,
@@ -12775,6 +12971,7 @@ export {
12775
12971
  normalizeDoclingDocument,
12776
12972
  normalizeDocumentSourceTreePaths,
12777
12973
  normalizeForMatch,
12974
+ normalizeOperationalPolicyTypes,
12778
12975
  normalizeSourceSpans,
12779
12976
  orderSourceEvidence,
12780
12977
  overlayTextOnPdf,
@@ -12785,6 +12982,7 @@ export {
12785
12982
  processReply,
12786
12983
  proposeContextWrites,
12787
12984
  resolveModelBudget,
12985
+ resolveOperationalProfilePolicyTypes,
12788
12986
  safeGenerateObject,
12789
12987
  sanitizeNulls,
12790
12988
  scoreCaseProposal,