@claritylabs/cl-sdk 1.3.3 → 1.3.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
@@ -908,6 +908,7 @@ var FormReferenceSchema = import_zod3.z.object({
908
908
  var TaxFeeItemSchema = import_zod3.z.object({
909
909
  name: import_zod3.z.string(),
910
910
  amount: import_zod3.z.string(),
911
+ amountValue: import_zod3.z.number().optional(),
911
912
  type: import_zod3.z.enum(["tax", "fee", "surcharge", "assessment"]).optional(),
912
913
  description: import_zod3.z.string().optional()
913
914
  });
@@ -952,9 +953,11 @@ var CoverageValueTypeSchema = import_zod4.z.enum([
952
953
  var CoverageSchema = import_zod4.z.object({
953
954
  name: import_zod4.z.string(),
954
955
  limit: import_zod4.z.string(),
956
+ limitAmount: import_zod4.z.number().optional(),
955
957
  limitType: LimitTypeSchema.optional(),
956
958
  limitValueType: CoverageValueTypeSchema.optional(),
957
959
  deductible: import_zod4.z.string().optional(),
960
+ deductibleAmount: import_zod4.z.number().optional(),
958
961
  deductibleValueType: CoverageValueTypeSchema.optional(),
959
962
  formNumber: import_zod4.z.string().optional(),
960
963
  pageNumber: import_zod4.z.number().optional(),
@@ -970,9 +973,11 @@ var EnrichedCoverageSchema = import_zod4.z.object({
970
973
  formNumber: import_zod4.z.string().optional(),
971
974
  formEditionDate: import_zod4.z.string().optional(),
972
975
  limit: import_zod4.z.string(),
976
+ limitAmount: import_zod4.z.number().optional(),
973
977
  limitType: LimitTypeSchema.optional(),
974
978
  limitValueType: CoverageValueTypeSchema.optional(),
975
979
  deductible: import_zod4.z.string().optional(),
980
+ deductibleAmount: import_zod4.z.number().optional(),
976
981
  deductibleType: DeductibleTypeSchema.optional(),
977
982
  deductibleValueType: CoverageValueTypeSchema.optional(),
978
983
  sir: import_zod4.z.string().optional(),
@@ -1653,7 +1658,8 @@ var UnderwritingConditionSchema = import_zod16.z.object({
1653
1658
  });
1654
1659
  var PremiumLineSchema = import_zod16.z.object({
1655
1660
  line: import_zod16.z.string(),
1656
- amount: import_zod16.z.string()
1661
+ amount: import_zod16.z.string(),
1662
+ amountValue: import_zod16.z.number().optional()
1657
1663
  });
1658
1664
  var AuxiliaryFactSchema = import_zod16.z.object({
1659
1665
  key: import_zod16.z.string(),
@@ -1696,6 +1702,7 @@ var BaseDocumentFields = {
1696
1702
  security: import_zod16.z.string().optional(),
1697
1703
  insuredName: import_zod16.z.string(),
1698
1704
  premium: import_zod16.z.string().optional(),
1705
+ premiumAmount: import_zod16.z.number().optional(),
1699
1706
  summary: import_zod16.z.string().optional(),
1700
1707
  policyTypes: import_zod16.z.array(import_zod16.z.string()).optional(),
1701
1708
  coverages: import_zod16.z.array(CoverageSchema),
@@ -1747,8 +1754,11 @@ var BaseDocumentFields = {
1747
1754
  mortgageHolders: import_zod16.z.array(EndorsementPartySchema).optional(),
1748
1755
  taxesAndFees: import_zod16.z.array(TaxFeeItemSchema).optional(),
1749
1756
  totalCost: import_zod16.z.string().optional(),
1757
+ totalCostAmount: import_zod16.z.number().optional(),
1750
1758
  minimumPremium: import_zod16.z.string().optional(),
1759
+ minimumPremiumAmount: import_zod16.z.number().optional(),
1751
1760
  depositPremium: import_zod16.z.string().optional(),
1761
+ depositPremiumAmount: import_zod16.z.number().optional(),
1752
1762
  paymentPlan: PaymentPlanSchema.optional(),
1753
1763
  auditType: AuditTypeSchema.optional(),
1754
1764
  ratingBasis: import_zod16.z.array(RatingBasisSchema).optional(),
@@ -3229,16 +3239,6 @@ function findFieldValue(fields, patterns, reject) {
3229
3239
  const match = fields.find((f) => fieldMatches(f.field, patterns) && !reject?.(f));
3230
3240
  return match?.value;
3231
3241
  }
3232
- function stringValue(value) {
3233
- return typeof value === "string" && value.trim() ? value : void 0;
3234
- }
3235
- function findRawString(raw, keys) {
3236
- for (const key of keys) {
3237
- const value = stringValue(raw[key]);
3238
- if (value) return value;
3239
- }
3240
- return void 0;
3241
- }
3242
3242
  function promoteRawFields(raw, mappings) {
3243
3243
  for (const { from, to } of mappings) {
3244
3244
  if (!raw[to] && raw[from]) {
@@ -3250,11 +3250,6 @@ function promoteRawFields(raw, mappings) {
3250
3250
  function findRawOrDeclarationValue(raw, fields, lookup) {
3251
3251
  return (lookup.rawKey ? raw[lookup.rawKey] : void 0) || findFieldValue(fields, lookup.patterns, lookup.reject);
3252
3252
  }
3253
- function promoteRawOrDeclarationString(raw, fields, targetKey, rawKeys, lookup) {
3254
- if (raw[targetKey]) return;
3255
- const value = findRawString(raw, rawKeys) ?? findFieldValue(fields, lookup.patterns, lookup.reject);
3256
- if (value) raw[targetKey] = value;
3257
- }
3258
3253
  function promoteCarrierFields(doc) {
3259
3254
  const raw = doc;
3260
3255
  promoteRawFields(raw, [
@@ -3454,298 +3449,11 @@ function promoteLocations(doc) {
3454
3449
  raw.locations = locations;
3455
3450
  }
3456
3451
  }
3457
- function normalizeName(name) {
3458
- return name.toLowerCase().replace(/[^a-z0-9]/g, "");
3459
- }
3460
- var LIMIT_COVERAGE_MAP = [
3461
- // GL standard
3462
- [["eachoccurrence", "peroccurrence", "occurrencecombined"], "perOccurrence"],
3463
- [["generalaggregate"], "generalAggregate"],
3464
- [["productscompletedoperationsaggregate", "productscompletedopsaggregate", "prodcompopsagg"], "productsCompletedOpsAggregate"],
3465
- [["personaladvertisinginjury", "personaladvinjury", "pai"], "personalAdvertisingInjury"],
3466
- [["firedamage", "firedamagelegalliability", "damagetorentedpremises", "damagetopremisesrentedtoyou"], "fireDamage"],
3467
- [["medicalexpense", "medexp", "medicalexpenseanypersonanyperson", "medicalexpenseanyone"], "medicalExpense"],
3468
- // Auto
3469
- [["combinedsingle", "combinedsinglelimit", "csl"], "combinedSingleLimit"],
3470
- [["bodilyinjuryperperson", "biperperson"], "bodilyInjuryPerPerson"],
3471
- [["bodilyinjuryperaccident", "biperaccident"], "bodilyInjuryPerAccident"],
3472
- [["propertydamage", "pdperaccident"], "propertyDamage"],
3473
- // Umbrella/Excess
3474
- [["umbrellaoccurrence", "eachoccurrenceumbrella", "excessoccurrence", "excesseachoccurrence"], "eachOccurrenceUmbrella"],
3475
- [["umbrellaaggregate", "excessaggregate"], "umbrellaAggregate"],
3476
- [["umbrella retention", "selfinsuredretention", "sir", "excessretention"], "umbrellaRetention"]
3477
- ];
3478
- function synthesizeLimits(doc) {
3479
- const raw = doc;
3480
- if (raw.limits && typeof raw.limits === "object" && Object.keys(raw.limits).length > 0) return;
3481
- const coverages = doc.coverages;
3482
- if (!coverages || coverages.length === 0) return;
3483
- const limits = {};
3484
- for (const cov of coverages) {
3485
- if (!cov.name || !cov.limit) continue;
3486
- const normalized = normalizeName(cov.name);
3487
- for (const [patterns, fieldName] of LIMIT_COVERAGE_MAP) {
3488
- if (patterns.some((p) => normalized.includes(p) || p.includes(normalized))) {
3489
- if (fieldName.includes("Aggregate") || fieldName.includes("aggregate")) {
3490
- if (!cov.limitType || cov.limitType === "aggregate") {
3491
- limits[fieldName] = cov.limit;
3492
- }
3493
- } else {
3494
- if (!limits[fieldName]) {
3495
- limits[fieldName] = cov.limit;
3496
- }
3497
- }
3498
- break;
3499
- }
3500
- }
3501
- }
3502
- const hasStatutory = coverages.some(
3503
- (c) => c.limitType === "statutory" || normalizeName(c.name ?? "").includes("statutory")
3504
- );
3505
- if (hasStatutory) {
3506
- limits.statutory = "true";
3507
- }
3508
- const elCoverages = coverages.filter(
3509
- (c) => normalizeName(c.name ?? "").includes("employersliability")
3510
- );
3511
- if (elCoverages.length > 0) {
3512
- const el = {};
3513
- for (const c of elCoverages) {
3514
- if (!c.limit) continue;
3515
- const n = normalizeName(c.name ?? "");
3516
- if (n.includes("accident") || n.includes("eachaccident")) el.eachAccident = c.limit;
3517
- else if (n.includes("diseasepolicy") || n.includes("diseasepolicylimit")) el.diseasePolicyLimit = c.limit;
3518
- else if (n.includes("diseaseemployee") || n.includes("diseaseeachemployee")) el.diseaseEachEmployee = c.limit;
3519
- else if (!el.eachAccident) el.eachAccident = c.limit;
3520
- }
3521
- if (Object.keys(el).length > 0) {
3522
- limits.employersLiability = el;
3523
- }
3524
- }
3525
- if (Object.keys(limits).length > 0) {
3526
- const result = { ...limits };
3527
- if (result.statutory === "true") result.statutory = true;
3528
- raw.limits = result;
3529
- }
3530
- }
3531
- function synthesizeDeductibles(doc) {
3532
- const raw = doc;
3533
- if (raw.deductibles && typeof raw.deductibles === "object" && Object.keys(raw.deductibles).length > 0) return;
3534
- const coverages = doc.coverages;
3535
- if (!coverages || coverages.length === 0) return;
3536
- const deductibleValues = coverages.filter((c) => c.deductible && c.deductible.trim() !== "" && c.deductible !== "N/A" && c.deductible !== "None").map((c) => c.deductible);
3537
- if (deductibleValues.length === 0) return;
3538
- const freq = /* @__PURE__ */ new Map();
3539
- for (const d of deductibleValues) {
3540
- freq.set(d, (freq.get(d) ?? 0) + 1);
3541
- }
3542
- let mostCommon = deductibleValues[0];
3543
- let maxFreq = 0;
3544
- for (const [val, count] of freq) {
3545
- if (count > maxFreq) {
3546
- mostCommon = val;
3547
- maxFreq = count;
3548
- }
3549
- }
3550
- const deductibles = {};
3551
- const hasPerClaim = coverages.some(
3552
- (c) => c.deductible && normalizeName(c.name ?? "").includes("perclaim")
3553
- );
3554
- if (hasPerClaim) {
3555
- deductibles.perClaim = mostCommon;
3556
- } else {
3557
- deductibles.perOccurrence = mostCommon;
3558
- }
3559
- const sirCoverage = coverages.find(
3560
- (c) => c.deductible && (normalizeName(c.name ?? "").includes("selfinsuredretention") || normalizeName(c.name ?? "").includes("sir"))
3561
- );
3562
- if (sirCoverage?.deductible) {
3563
- deductibles.selfInsuredRetention = sirCoverage.deductible;
3564
- }
3565
- const aggDed = coverages.find(
3566
- (c) => c.deductible && normalizeName(c.name ?? "").includes("aggregatedeductible")
3567
- );
3568
- if (aggDed?.deductible) {
3569
- deductibles.aggregateDeductible = aggDed.deductible;
3570
- }
3571
- if (Object.keys(deductibles).length > 0) {
3572
- raw.deductibles = deductibles;
3573
- }
3574
- }
3575
- var PREMIUM_PATTERNS = [
3576
- "premium",
3577
- "premiumAmount",
3578
- "premium amount",
3579
- "totalPremium",
3580
- "total premium",
3581
- "totalPolicyPremium",
3582
- "total policy premium",
3583
- "annualPremium",
3584
- "annual premium",
3585
- "estimatedAnnualPremium",
3586
- "estimated annual premium",
3587
- "policyPremium",
3588
- "policy premium",
3589
- "basePremium",
3590
- "base premium",
3591
- "planCost",
3592
- "plan cost",
3593
- "policyCost",
3594
- "policy cost",
3595
- "premiumSubtotal",
3596
- "premium subtotal",
3597
- "subtotalPremium",
3598
- "subtotal premium",
3599
- "quotedPremium",
3600
- "quoted premium"
3601
- ];
3602
- var TOTAL_COST_PATTERNS = [
3603
- "totalCost",
3604
- "total cost",
3605
- "total",
3606
- "totalDue",
3607
- "total due",
3608
- "amountPaid",
3609
- "amount paid",
3610
- "totalPaid",
3611
- "total paid",
3612
- "totalPrice",
3613
- "total price",
3614
- "totalTripCost",
3615
- "total trip cost",
3616
- "amountCharged",
3617
- "amount charged",
3618
- "amountDue",
3619
- "amount due",
3620
- "totalAmountDue",
3621
- "total amount due",
3622
- "totalAmount",
3623
- "total amount",
3624
- "grandTotal",
3625
- "grand total",
3626
- "totalPayable",
3627
- "total payable",
3628
- "totalCharges",
3629
- "total charges",
3630
- "totalPolicyCost",
3631
- "total policy cost"
3632
- ];
3633
- var PREMIUM_RAW_KEYS = [
3634
- "premium",
3635
- "premiumAmount",
3636
- "premium_amount",
3637
- "totalPremium",
3638
- "totalPolicyPremium",
3639
- "annualPremium",
3640
- "estimatedAnnualPremium",
3641
- "policyPremium",
3642
- "basePremium",
3643
- "planCost",
3644
- "policyCost",
3645
- "premiumSubtotal",
3646
- "subtotalPremium",
3647
- "quotedPremium"
3648
- ];
3649
- var TOTAL_COST_RAW_KEYS = [
3650
- "totalCost",
3651
- "total_cost",
3652
- "total",
3653
- "totalDue",
3654
- "amountPaid",
3655
- "amount_paid",
3656
- "totalPaid",
3657
- "total_paid",
3658
- "totalPrice",
3659
- "totalTripCost",
3660
- "amountCharged",
3661
- "amountDue",
3662
- "totalAmountDue",
3663
- "totalAmount",
3664
- "grandTotal",
3665
- "totalPayable",
3666
- "totalCharges",
3667
- "totalPolicyCost"
3668
- ];
3669
- function isTaxOrFeeField(fieldName) {
3670
- const normalized = normalizeFieldName(fieldName);
3671
- return /tax|gst|hst|pst|qst|fee|surcharge|assessment|stamp|filing|inspection/.test(normalized);
3672
- }
3673
- function isTotalCostField(fieldName) {
3674
- return fieldMatches(fieldName, TOTAL_COST_PATTERNS);
3675
- }
3676
- function taxFeeType(fieldName) {
3677
- const normalized = normalizeFieldName(fieldName);
3678
- if (normalized.includes("tax") || ["gst", "hst", "pst", "qst"].some((token) => normalized.includes(token))) return "tax";
3679
- if (normalized.includes("surcharge")) return "surcharge";
3680
- if (normalized.includes("assessment")) return "assessment";
3681
- if (normalized.includes("fee") || normalized.includes("stamp") || normalized.includes("filing")) return "fee";
3682
- return void 0;
3683
- }
3684
- function titleizeFieldName(fieldName) {
3685
- const spaced = fieldName.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_-]+/g, " ").replace(/\s+/g, " ").trim();
3686
- return spaced.replace(/\b\w/g, (letter) => letter.toUpperCase());
3687
- }
3688
- function taxFeeKey(item) {
3689
- return [
3690
- normalizeFieldName(item.name),
3691
- normalizeFieldName(item.amount),
3692
- item.type ?? ""
3693
- ].join("|");
3694
- }
3695
- function taxFeeItemFromField(field) {
3696
- const type = taxFeeType(field.field);
3697
- return {
3698
- name: titleizeFieldName(field.field),
3699
- amount: absorbNegative(field.value),
3700
- ...type ? { type } : {}
3701
- };
3702
- }
3703
- function absorbNegative(value) {
3704
- return value.replace(/^-\s*/, "").replace(/^\(\s*(.*?)\s*\)$/, "$1");
3705
- }
3706
- function promotePremium(doc) {
3707
- const raw = doc;
3708
- const fields = getDeclarationFields(doc);
3709
- promoteRawOrDeclarationString(raw, fields, "premium", PREMIUM_RAW_KEYS, {
3710
- patterns: PREMIUM_PATTERNS,
3711
- reject: (field) => isTaxOrFeeField(field.field)
3712
- });
3713
- promoteRawOrDeclarationString(raw, fields, "totalCost", TOTAL_COST_RAW_KEYS, {
3714
- patterns: TOTAL_COST_PATTERNS
3715
- });
3716
- if (typeof raw.premium === "string") raw.premium = absorbNegative(raw.premium);
3717
- if (typeof raw.totalCost === "string") raw.totalCost = absorbNegative(raw.totalCost);
3718
- }
3719
- function synthesizeTaxesAndFees(doc) {
3720
- const raw = doc;
3721
- const fields = getDeclarationFields(doc);
3722
- if (fields.length === 0) return;
3723
- const existing = Array.isArray(raw.taxesAndFees) ? raw.taxesAndFees : [];
3724
- const byKey = /* @__PURE__ */ new Map();
3725
- for (const item of existing) {
3726
- if (!item?.name || !item?.amount) continue;
3727
- byKey.set(taxFeeKey(item), item);
3728
- }
3729
- for (const field of fields) {
3730
- if (!field.value?.trim()) continue;
3731
- if (!isTaxOrFeeField(field.field)) continue;
3732
- if (isTotalCostField(field.field)) continue;
3733
- const item = taxFeeItemFromField(field);
3734
- byKey.set(taxFeeKey(item), item);
3735
- }
3736
- if (byKey.size > 0) {
3737
- raw.taxesAndFees = [...byKey.values()];
3738
- }
3739
- }
3740
3452
  function promoteExtractedFields(doc) {
3741
3453
  promoteCarrierFields(doc);
3742
3454
  promoteBroker(doc);
3743
3455
  promoteLossPayees(doc);
3744
3456
  promoteLocations(doc);
3745
- synthesizeLimits(doc);
3746
- synthesizeDeductibles(doc);
3747
- synthesizeTaxesAndFees(doc);
3748
- promotePremium(doc);
3749
3457
  }
3750
3458
 
3751
3459
  // src/extraction/alignment.ts
@@ -6468,6 +6176,12 @@ Focus on:
6468
6176
 
6469
6177
  Look on the declarations page, named insured schedule, loss payee schedule, mortgagee schedule, and any endorsements that add or modify named insureds, loss payees, or mortgage holders.
6470
6178
 
6179
+ Critical rules:
6180
+ - Prefer declaration-table labels such as "Named Insured", "Named Insured and Address", "Applicant", or "Insured" over contact blocks, notice contacts, authorized officers, licensing statements, signatures, and corporate-authority wording.
6181
+ - Do not use an authorized officer, broker, producer, contact person, officer title, email address owner, or licensing/entity-status statement as the primary insured unless that exact person/entity is explicitly labeled as the named insured.
6182
+ - If a row combines the insured name with a mailing address, put the legal name in insuredName and the mailing address in insuredAddress.
6183
+ - Entity type must come from the insured's own legal suffix or an explicit declaration field, not from generic incorporation/licensing notices elsewhere in the policy.
6184
+
6471
6185
  Return JSON only.`;
6472
6186
  }
6473
6187
 
@@ -6501,7 +6215,9 @@ For EACH coverage, also extract:
6501
6215
  - sectionRef: the declarations/schedule/endorsement section heading where it appears
6502
6216
  - originalContent: the verbatim row or short source snippet used for this coverage
6503
6217
  - limitType: when applicable, classify the limit as per_occurrence, per_claim, aggregate, per_person, per_accident, statutory, blanket, or scheduled
6218
+ - limitAmount: the limit as a plain number with no currency symbols or commas when the source states a fixed numeric currency amount
6504
6219
  - limitValueType: classify the limit as numeric, included, not_included, as_stated, waiting_period, referential, or other
6220
+ - deductibleAmount: the deductible or retention as a plain number with no currency symbols or commas when the source states a fixed numeric currency amount
6505
6221
  - deductibleValueType: classify the deductible/value term similarly when deductible is present
6506
6222
 
6507
6223
  Critical rules:
@@ -6510,6 +6226,7 @@ Critical rules:
6510
6226
  - Do not treat a generic waiting period, deductible explanation, limits clause, coinsurance clause, or definitions text as a standalone coverage unless the page contains an actual policy-specific schedule row or declaration entry.
6511
6227
  - Values like "Included" or "Not Included" are valid only when they appear as an explicit declarations/schedule/endorsement entry for a named coverage. Do not infer them from narrative form language.
6512
6228
  - If a waiting period or hour deductible is shown as part of a specific declarations/schedule row, it may be captured in deductible. Otherwise omit it.
6229
+ - Only populate limitAmount and deductibleAmount from actual policy-specific declaration, schedule, or endorsement values. Do not calculate them from examples, definitions, rating narratives, or generic form language.
6513
6230
  - Use limitValueType or deductibleValueType to preserve non-numeric terms precisely instead of forcing them into numeric semantics.
6514
6231
  - Preserve one row per real coverage entry. Do not merge adjacent schedule rows into malformed names.
6515
6232
  - Keep individual/per-occurrence limits separate from aggregate limits even when they have the same coverage name, limit amount, deductible, and form number. Use limitType to distinguish them.
@@ -6729,22 +6446,28 @@ Return JSON only.`;
6729
6446
  var import_zod32 = require("zod");
6730
6447
  var PremiumBreakdownSchema = import_zod32.z.object({
6731
6448
  premium: import_zod32.z.string().optional().describe("Total premium amount, e.g. '$5,000'"),
6449
+ premiumAmount: import_zod32.z.number().optional().describe("Total premium as a plain number with no currency symbols or commas"),
6732
6450
  totalCost: import_zod32.z.string().optional().describe("Total cost including taxes and fees, e.g. '$5,250'"),
6451
+ totalCostAmount: import_zod32.z.number().optional().describe("Total cost as a plain number with no currency symbols or commas"),
6733
6452
  premiumBreakdown: import_zod32.z.array(
6734
6453
  import_zod32.z.object({
6735
6454
  line: import_zod32.z.string().describe("Coverage line name"),
6736
- amount: import_zod32.z.string().describe("Premium amount for this line")
6455
+ amount: import_zod32.z.string().describe("Premium amount for this line"),
6456
+ amountValue: import_zod32.z.number().optional().describe("Premium amount as a plain number with no currency symbols or commas")
6737
6457
  })
6738
6458
  ).optional().describe("Per-coverage-line premium breakdown"),
6739
6459
  taxesAndFees: import_zod32.z.array(
6740
6460
  import_zod32.z.object({
6741
6461
  name: import_zod32.z.string().describe("Fee or tax name"),
6742
6462
  amount: import_zod32.z.string().describe("Dollar amount"),
6463
+ amountValue: import_zod32.z.number().optional().describe("Fee or tax amount as a plain number with no currency symbols or commas"),
6743
6464
  type: import_zod32.z.enum(["tax", "fee", "surcharge", "assessment"]).optional().describe("Fee category")
6744
6465
  })
6745
6466
  ).optional().describe("Taxes, fees, surcharges, and assessments"),
6746
6467
  minimumPremium: import_zod32.z.string().optional().describe("Minimum premium if stated"),
6468
+ minimumPremiumAmount: import_zod32.z.number().optional().describe("Minimum premium as a plain number when the source states a fixed amount"),
6747
6469
  depositPremium: import_zod32.z.string().optional().describe("Deposit premium if stated"),
6470
+ depositPremiumAmount: import_zod32.z.number().optional().describe("Deposit premium as a plain number when the source states a fixed amount"),
6748
6471
  paymentPlan: import_zod32.z.string().optional().describe("Payment plan description"),
6749
6472
  auditType: import_zod32.z.enum(["annual", "semi_annual", "quarterly", "monthly", "final", "self"]).optional().describe("Premium audit type"),
6750
6473
  ratingBasis: import_zod32.z.string().optional().describe("Rating basis, e.g. payroll, revenue, area, units")
@@ -6754,6 +6477,7 @@ function buildPremiumBreakdownPrompt() {
6754
6477
 
6755
6478
  Focus on:
6756
6479
  - Total premium and total cost (including taxes/fees)
6480
+ - Plain numeric amount fields for stated money values, without currency symbols or commas
6757
6481
  - Per-coverage-line premium breakdown if available
6758
6482
  - Taxes, fees, surcharges, and assessments with their amounts and types
6759
6483
  - Minimum premium and deposit premium if stated
@@ -6762,6 +6486,7 @@ Focus on:
6762
6486
  - Rating basis: payroll, revenue, area, units, or other
6763
6487
 
6764
6488
  Look on the declarations page, premium summary, and any premium/cost schedules.
6489
+ Prefer premium tables and schedules over definitions, exclusions, rating-basis narratives, licensing statements, or descriptions of premium trust funds. Do not use unrelated business volume, controlled written premium, deductible, limit, tax-only, fee-only, or percentage-only values as the policy premium.
6765
6490
 
6766
6491
  Return JSON only.`;
6767
6492
  }
@@ -8169,41 +7894,11 @@ function toReviewRoundRecord(round, review) {
8169
7894
  }
8170
7895
 
8171
7896
  // src/extraction/planning.ts
8172
- function normalizePageAssignments(pageAssignments, formInventory) {
8173
- const pageFormTypes = /* @__PURE__ */ new Map();
8174
- if (formInventory) {
8175
- for (const form of formInventory.forms) {
8176
- if (form.pageStart != null) {
8177
- const end = form.pageEnd ?? form.pageStart;
8178
- for (let p = form.pageStart; p <= end; p += 1) {
8179
- const types = pageFormTypes.get(p) ?? /* @__PURE__ */ new Set();
8180
- types.add(form.formType);
8181
- pageFormTypes.set(p, types);
8182
- }
8183
- }
8184
- }
8185
- }
7897
+ function normalizePageAssignments(pageAssignments, _formInventory) {
8186
7898
  return pageAssignments.map((assignment) => {
8187
- let extractorNames = [...new Set(
7899
+ const extractorNames = [...new Set(
8188
7900
  assignment.extractorNames.filter(Boolean)
8189
7901
  )];
8190
- const hasDeclarations = extractorNames.includes("declarations");
8191
- const hasConditions = extractorNames.includes("conditions");
8192
- const hasExclusions = extractorNames.includes("exclusions");
8193
- const hasEndorsements = extractorNames.includes("endorsements");
8194
- const looksLikeScheduleValues = assignment.hasScheduleValues === true;
8195
- const roleBlocksCoverageLimits = assignment.pageRole === "policy_form" || assignment.pageRole === "condition_exclusion_form" || assignment.pageRole === "endorsement_form";
8196
- const inventoryTypes = pageFormTypes.get(assignment.localPageNumber);
8197
- const inventoryBlocksCoverageLimits = inventoryTypes != null && !looksLikeScheduleValues && !hasDeclarations && (inventoryTypes.has("endorsement") || inventoryTypes.has("notice") || inventoryTypes.has("application"));
8198
- if (extractorNames.includes("coverage_limits")) {
8199
- const shouldDropCoverageLimits = inventoryBlocksCoverageLimits || !looksLikeScheduleValues && roleBlocksCoverageLimits || !hasDeclarations && !looksLikeScheduleValues && (hasConditions || hasExclusions) || !hasDeclarations && !looksLikeScheduleValues && hasEndorsements;
8200
- if (shouldDropCoverageLimits) {
8201
- extractorNames = extractorNames.filter((name) => name !== "coverage_limits");
8202
- }
8203
- }
8204
- if (inventoryTypes?.has("endorsement") && !extractorNames.includes("endorsements")) {
8205
- extractorNames = [...extractorNames, "endorsements"];
8206
- }
8207
7902
  return {
8208
7903
  ...assignment,
8209
7904
  extractorNames
@@ -8542,6 +8237,18 @@ function createExtractor(config) {
8542
8237
  return typeof start === "number" && typeof end === "number" && start <= endPage && end >= startPage;
8543
8238
  });
8544
8239
  }
8240
+ function formatSourceSpanText(spans) {
8241
+ return spans.filter((span) => span.text.trim().length > 0).map((span) => {
8242
+ const page = pageNumberForSpan(span);
8243
+ const label = [
8244
+ page ? `Page ${page}` : void 0,
8245
+ span.sectionId,
8246
+ span.formNumber
8247
+ ].filter(Boolean).join(" | ");
8248
+ return label ? `${label}
8249
+ ${span.text}` : span.text;
8250
+ }).join("\n\n---\n\n");
8251
+ }
8545
8252
  function inferSectionType(title, text) {
8546
8253
  const value = `${title} ${text.slice(0, 500)}`.toLowerCase();
8547
8254
  if (/\bdefinition|defined terms?\b/.test(value)) return "definition";
@@ -8578,93 +8285,6 @@ function createExtractor(config) {
8578
8285
  if (!line) return void 0;
8579
8286
  return line.slice(0, 100);
8580
8287
  }
8581
- function buildDeterministicFormInventory(spans, pageCount) {
8582
- if (spans.length === 0) return void 0;
8583
- const formPattern = /\b([A-Z]{1,4}\s?\d{2,5}(?:\s?\d{2,4})?|[A-Z]{2,8}-\d{2,8})\b/g;
8584
- const seen = /* @__PURE__ */ new Map();
8585
- for (const span of spans) {
8586
- const page = pageNumberForSpan(span);
8587
- if (!page) continue;
8588
- const title = firstHeadingLine(span.text) ?? span.sectionId;
8589
- const matches = /* @__PURE__ */ new Set([
8590
- ...span.formNumber ? [span.formNumber] : [],
8591
- ...Array.from(span.text.slice(0, 1200).matchAll(formPattern), (match) => match[1].trim())
8592
- ]);
8593
- for (const formNumber of matches) {
8594
- const key = `${formNumber}:${title ?? ""}`;
8595
- const existing = seen.get(key);
8596
- const formType = inferFormType(title ?? "", span.text);
8597
- if (existing) {
8598
- existing.pageStart = Math.min(existing.pageStart ?? page, page);
8599
- existing.pageEnd = Math.max(existing.pageEnd ?? page, page);
8600
- continue;
8601
- }
8602
- seen.set(key, {
8603
- formNumber,
8604
- title,
8605
- formType,
8606
- pageStart: Math.min(page, pageCount),
8607
- pageEnd: Math.min(page, pageCount)
8608
- });
8609
- }
8610
- }
8611
- const forms = [...seen.values()].sort((a, b) => (a.pageStart ?? 0) - (b.pageStart ?? 0));
8612
- return forms.length > 0 ? { forms } : void 0;
8613
- }
8614
- function inferFormType(title, text) {
8615
- const value = `${title} ${text.slice(0, 700)}`.toLowerCase();
8616
- if (/\bdeclarations?|schedule\b/.test(value)) return "declarations";
8617
- if (/\bendorsement|amendatory|additional insured\b/.test(value)) return "endorsement";
8618
- if (/\bnotice|department of insurance|complaint|privacy\b/.test(value)) return "notice";
8619
- if (/\bapplication|questionnaire\b/.test(value)) return "application";
8620
- if (/\bcoverage|policy form|conditions|exclusions|definitions|causes of loss\b/.test(value)) return "coverage";
8621
- return "other";
8622
- }
8623
- function buildDeterministicPageAssignments(spans, pageCount) {
8624
- if (spans.length === 0) return [];
8625
- const pageTexts = /* @__PURE__ */ new Map();
8626
- for (const span of spans) {
8627
- const page = pageNumberForSpan(span);
8628
- if (!page) continue;
8629
- pageTexts.set(page, [pageTexts.get(page), span.sectionId, span.text].filter(Boolean).join("\n"));
8630
- }
8631
- return Array.from({ length: pageCount }, (_, index) => {
8632
- const page = index + 1;
8633
- const text = pageTexts.get(page) ?? "";
8634
- const lower = text.toLowerCase();
8635
- const extractorNames = [];
8636
- if (/\bdeclarations?|policy period|named insured|producer|schedule\b/.test(lower)) {
8637
- extractorNames.push("carrier_info", "named_insured", "declarations");
8638
- }
8639
- if (/\blimit|deductible|premium|coinsurance|scheduled amount|blanket\b/.test(lower) && /\$|\b\d{2,}(?:,\d{3})*\b/.test(lower)) {
8640
- extractorNames.push("coverage_limits");
8641
- }
8642
- if (/\bpremium|tax|fee|surcharge\b/.test(lower) && /\$/.test(lower)) extractorNames.push("premium_breakdown");
8643
- if (/\bendorsement|additional insured|amendatory\b/.test(lower)) extractorNames.push("endorsements");
8644
- if (/\bexclusion|not covered|does not apply\b/.test(lower)) extractorNames.push("exclusions");
8645
- if (/\bcondition|duties in the event|loss condition|general condition\b/.test(lower)) extractorNames.push("conditions");
8646
- if (/\bdefinition|defined terms?\b/.test(lower)) extractorNames.push("definitions");
8647
- if (/\bcovered cause|covered peril|cause of loss|perils insured\b/.test(lower)) extractorNames.push("covered_reasons");
8648
- if (textIncludesSupplementarySignal(text)) extractorNames.push("supplementary");
8649
- if (extractorNames.length === 0 && text.trim().length > 180) extractorNames.push("sections");
8650
- return {
8651
- localPageNumber: page,
8652
- extractorNames: [...new Set(extractorNames)],
8653
- pageRole: inferPageRole(lower),
8654
- hasScheduleValues: /\$|\b\d{2,}(?:,\d{3})*\b/.test(lower) && /\blimit|deductible|premium|schedule|class|location\b/.test(lower),
8655
- confidence: text.trim().length > 0 ? 0.8 : 0,
8656
- notes: "Deterministic source-span page assignment"
8657
- };
8658
- });
8659
- }
8660
- function inferPageRole(lowerText) {
8661
- if (/\bdeclarations?|schedule\b/.test(lowerText)) return "declarations_schedule";
8662
- if (/\bendorsement\b/.test(lowerText)) return "endorsement_form";
8663
- if (/\bexclusion|condition\b/.test(lowerText)) return "condition_exclusion_form";
8664
- if (/\bnotice|department of insurance|complaint\b/.test(lowerText)) return "supplementary";
8665
- if (lowerText.trim()) return "policy_form";
8666
- return "other";
8667
- }
8668
8288
  function shouldRunLlmReview(mode, report, sourceSpansAvailable) {
8669
8289
  if (mode === "skip" || maxReviewRounds <= 0) return false;
8670
8290
  if (mode === "always") return true;
@@ -8885,21 +8505,28 @@ function createExtractor(config) {
8885
8505
  return promise;
8886
8506
  }
8887
8507
  async function getPageRangeText(startPage, endPage) {
8888
- return doclingDocument ? getDoclingPageRangeText(doclingDocument, startPage, endPage) : "";
8508
+ if (doclingDocument) return getDoclingPageRangeText(doclingDocument, startPage, endPage);
8509
+ return formatSourceSpanText(spansForPageRange(sourceSpans, startPage, endPage));
8889
8510
  }
8890
8511
  function withFullDocumentTextContext(prompt) {
8891
- if (!doclingDocument) return prompt;
8892
- return `${prompt}
8512
+ if (doclingDocument) return `${prompt}
8893
8513
 
8894
8514
  DOCLING DOCUMENT TEXT:
8895
8515
  ${doclingDocument.fullText}`;
8516
+ const sourceText = formatSourceSpanText(sourceSpans);
8517
+ if (!sourceText) return prompt;
8518
+ return `${prompt}
8519
+
8520
+ SOURCE SPAN DOCUMENT TEXT:
8521
+ ${sourceText}`;
8896
8522
  }
8897
8523
  function withPageRangeTextContext(prompt, startPage, endPage, pageText) {
8898
- if (!doclingDocument) return prompt;
8524
+ if (!pageText) return prompt;
8525
+ const label = doclingDocument ? "DOCLING DOCUMENT" : "SOURCE SPAN DOCUMENT";
8899
8526
  return `${prompt}
8900
8527
 
8901
- DOCLING DOCUMENT PAGES ${startPage}-${endPage}:
8902
- ${pageText || "(No Docling text was available for this page range.)"}`;
8528
+ ${label} PAGES ${startPage}-${endPage}:
8529
+ ${pageText}`;
8903
8530
  }
8904
8531
  let classifyResult;
8905
8532
  if (resumed?.classifyResult && pipelineCtx.isPhaseComplete("classify")) {
@@ -8956,17 +8583,6 @@ ${pageText || "(No Docling text was available for this page range.)"}`;
8956
8583
  formInventory = resumed.formInventory;
8957
8584
  memory.set("form_inventory", formInventory);
8958
8585
  onProgress?.("Resuming from checkpoint (form inventory complete)...");
8959
- } else if (sourceSpans.length > 0) {
8960
- formInventory = buildDeterministicFormInventory(sourceSpans, pageCount) ?? { forms: [] };
8961
- onProgress?.(`Built deterministic form inventory for ${primaryType} ${documentType}.`);
8962
- memory.set("form_inventory", formInventory);
8963
- await pipelineCtx.save("form_inventory", {
8964
- id,
8965
- pageCount,
8966
- classifyResult,
8967
- formInventory,
8968
- memory: Object.fromEntries(memory)
8969
- });
8970
8586
  } else {
8971
8587
  onProgress?.(`Building form inventory for ${primaryType} ${documentType}...`);
8972
8588
  const budget = resolveBudget("extraction_form_inventory", 2048);
@@ -9007,20 +8623,6 @@ ${pageText || "(No Docling text was available for this page range.)"}`;
9007
8623
  if (resumed?.pageAssignments && pipelineCtx.isPhaseComplete("page_map")) {
9008
8624
  pageAssignments = resumed.pageAssignments;
9009
8625
  onProgress?.("Resuming from checkpoint (page map complete)...");
9010
- } else if (sourceSpans.length > 0) {
9011
- onProgress?.(`Mapping document pages from source spans for ${primaryType} ${documentType}...`);
9012
- pageAssignments = normalizePageAssignments(
9013
- buildDeterministicPageAssignments(sourceSpans, pageCount),
9014
- formInventory
9015
- );
9016
- await pipelineCtx.save("page_map", {
9017
- id,
9018
- pageCount,
9019
- classifyResult,
9020
- formInventory,
9021
- pageAssignments,
9022
- memory: Object.fromEntries(memory)
9023
- });
9024
8626
  } else {
9025
8627
  onProgress?.(`Mapping document pages for ${primaryType} ${documentType}...`);
9026
8628
  const chunkSize = 8;
@@ -9037,7 +8639,7 @@ ${pageText || "(No Docling text was available for this page range.)"}`;
9037
8639
  pageMapChunks.map(
9038
8640
  ({ startPage, endPage }) => pageMapLimit(async () => {
9039
8641
  const pagesPdf = doclingDocument ? void 0 : await getPageRangePdf(startPage, endPage);
9040
- const pagesText = doclingDocument ? await getPageRangeText(startPage, endPage) : "";
8642
+ const pagesText = await getPageRangeText(startPage, endPage);
9041
8643
  const budget = resolveBudget("extraction_page_map", 2048);
9042
8644
  const startedAt = Date.now();
9043
8645
  const mapResponse = await safeGenerateObject(
@@ -9144,7 +8746,7 @@ ${pageText || "(No Docling text was available for this page range.)"}`;
9144
8746
  completedPageRangePdfCache,
9145
8747
  getPageRangePdf,
9146
8748
  convertPdfToImages ? getPageImages : void 0,
9147
- doclingDocument ? getPageRangeText : void 0
8749
+ sourceSpans.length > 0 || doclingDocument ? getPageRangeText : void 0
9148
8750
  );
9149
8751
  })
9150
8752
  )
@@ -9177,7 +8779,7 @@ ${pageText || "(No Docling text was available for this page range.)"}`;
9177
8779
  pageRangeCache: completedPageRangePdfCache,
9178
8780
  getPageRangePdf,
9179
8781
  getPageImages: convertPdfToImages ? getPageImages : void 0,
9180
- getPageRangeText: doclingDocument ? getPageRangeText : void 0
8782
+ getPageRangeText: sourceSpans.length > 0 || doclingDocument ? getPageRangeText : void 0
9181
8783
  });
9182
8784
  trackUsage(supplementaryResult.usage, {
9183
8785
  taskKind: "extraction_focused",
@@ -9213,7 +8815,7 @@ ${pageText || "(No Docling text was available for this page range.)"}`;
9213
8815
  concurrency,
9214
8816
  getPageRangePdf,
9215
8817
  getPageImages: convertPdfToImages ? getPageImages : void 0,
9216
- getPageRangeText: doclingDocument ? getPageRangeText : void 0,
8818
+ getPageRangeText: sourceSpans.length > 0 || doclingDocument ? getPageRangeText : void 0,
9217
8819
  providerOptions: activeProviderOptions,
9218
8820
  modelCapabilities,
9219
8821
  modelBudgetConstraints,
@@ -9309,7 +8911,7 @@ ${pageText || "(No Docling text was available for this page range.)"}`;
9309
8911
  completedPageRangePdfCache,
9310
8912
  getPageRangePdf,
9311
8913
  convertPdfToImages ? getPageImages : void 0,
9312
- doclingDocument ? getPageRangeText : void 0
8914
+ sourceSpans.length > 0 || doclingDocument ? getPageRangeText : void 0
9313
8915
  );
9314
8916
  })
9315
8917
  )