@claritylabs/cl-sdk 4.1.0 → 4.3.0

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
@@ -277,6 +277,7 @@ __export(src_exports, {
277
277
  VerifyResultSchema: () => VerifyResultSchema,
278
278
  WatercraftDeclarationsSchema: () => WatercraftDeclarationsSchema,
279
279
  WorkersCompDeclarationsSchema: () => WorkersCompDeclarationsSchema,
280
+ annotateOperationalCoverageLinesOfBusiness: () => annotateOperationalCoverageLinesOfBusiness,
280
281
  applyApplicationAnswers: () => applyApplicationAnswers,
281
282
  buildAcroFormMappingPrompt: () => buildAcroFormMappingPrompt,
282
283
  buildAgentSystemPrompt: () => buildAgentSystemPrompt,
@@ -343,6 +344,7 @@ __export(src_exports, {
343
344
  getNextApplicationQuestions: () => getNextApplicationQuestions,
344
345
  getPdfPageCount: () => getPdfPageCount,
345
346
  getTemplate: () => getTemplate,
347
+ inferLineOfBusinessForOperationalCoverage: () => inferLineOfBusinessForOperationalCoverage,
346
348
  inferLinesOfBusinessFromOperationalCoverages: () => inferLinesOfBusinessFromOperationalCoverages,
347
349
  isDoclingExtractionInput: () => isDoclingExtractionInput,
348
350
  isFileReference: () => isFileReference,
@@ -1449,7 +1451,8 @@ var InsurerInfoSchema = import_zod9.z.object({
1449
1451
  amBestRating: import_zod9.z.string().optional(),
1450
1452
  amBestNumber: import_zod9.z.string().optional(),
1451
1453
  admittedStatus: AdmittedStatusSchema.optional(),
1452
- stateOfDomicile: import_zod9.z.string().optional()
1454
+ stateOfDomicile: import_zod9.z.string().optional(),
1455
+ address: AddressSchema.optional()
1453
1456
  }).merge(SourceProvenanceSchema);
1454
1457
  var ProducerInfoSchema = import_zod9.z.object({
1455
1458
  agencyName: import_zod9.z.string(),
@@ -2805,6 +2808,7 @@ var OperationalCoverageTermSchema = import_zod21.z.object({
2805
2808
  });
2806
2809
  var OperationalCoverageLineSchema = import_zod21.z.object({
2807
2810
  name: import_zod21.z.string(),
2811
+ lineOfBusiness: AcordLobCodeSchema.optional(),
2808
2812
  coverageCode: import_zod21.z.string().optional(),
2809
2813
  limit: import_zod21.z.string().optional(),
2810
2814
  deductible: import_zod21.z.string().optional(),
@@ -2820,6 +2824,7 @@ var OperationalCoverageLineSchema = import_zod21.z.object({
2820
2824
  var OperationalPartySchema = import_zod21.z.object({
2821
2825
  role: import_zod21.z.string(),
2822
2826
  name: import_zod21.z.string(),
2827
+ address: OperationalAddressSchema.optional(),
2823
2828
  sourceNodeIds: import_zod21.z.array(import_zod21.z.string().min(1)).default([]),
2824
2829
  sourceSpanIds: import_zod21.z.array(import_zod21.z.string().min(1)).default([])
2825
2830
  });
@@ -2857,6 +2862,7 @@ var PolicyOperationalProfileSchema = import_zod21.z.preprocess(
2857
2862
  expirationDate: SourceBackedValueSchema.optional(),
2858
2863
  retroactiveDate: SourceBackedValueSchema.optional(),
2859
2864
  premium: SourceBackedValueSchema.optional(),
2865
+ operationsDescription: SourceBackedValueSchema.optional(),
2860
2866
  declarationFacts: import_zod21.z.array(OperationalDeclarationFactSchema).default([]),
2861
2867
  coverages: import_zod21.z.array(OperationalCoverageLineSchema).default([]),
2862
2868
  parties: import_zod21.z.array(OperationalPartySchema).default([]),
@@ -2914,6 +2920,9 @@ function normalizeWhitespace(value) {
2914
2920
  function hasSpecificLineOfBusiness(codes) {
2915
2921
  return codes.some((code) => code !== "UN");
2916
2922
  }
2923
+ function specificLineOfBusinessCodes(codes) {
2924
+ return Array.from(new Set(codes.filter((code) => code !== "UN")));
2925
+ }
2917
2926
  function linesOfBusinessFromText(value) {
2918
2927
  const text = normalizeWhitespace(value ?? "");
2919
2928
  if (!text) return [];
@@ -2922,20 +2931,53 @@ function linesOfBusinessFromText(value) {
2922
2931
  const inferred = LOB_TEXT_PATTERNS.flatMap(({ codes, pattern }) => pattern.test(text) ? codes : []);
2923
2932
  return Array.from(new Set(inferred));
2924
2933
  }
2934
+ function coverageLineOfBusinessCandidates(coverage) {
2935
+ const limits = coverage.limits ?? [];
2936
+ const text = [
2937
+ coverage.coverageCode,
2938
+ coverage.formNumber,
2939
+ coverage.sectionRef,
2940
+ coverage.name,
2941
+ ...limits.map((term) => term.appliesTo)
2942
+ ].filter((value) => typeof value === "string" && value.trim().length > 0);
2943
+ const inferred = [];
2944
+ for (const value of text) {
2945
+ for (const code of linesOfBusinessFromText(value)) {
2946
+ if (code !== "UN" && !inferred.includes(code)) inferred.push(code);
2947
+ }
2948
+ }
2949
+ return inferred;
2950
+ }
2951
+ function explicitCoverageLineOfBusiness(coverage) {
2952
+ if (!coverage.lineOfBusiness) return void 0;
2953
+ const [code] = normalizeOperationalLinesOfBusiness([coverage.lineOfBusiness]);
2954
+ return code && code !== "UN" ? code : void 0;
2955
+ }
2956
+ function inferLineOfBusinessForOperationalCoverage(coverage, profileLinesOfBusiness) {
2957
+ const explicit = explicitCoverageLineOfBusiness(coverage);
2958
+ if (explicit) return explicit;
2959
+ const inferred = coverageLineOfBusinessCandidates(coverage);
2960
+ if (inferred.length === 1) return inferred[0];
2961
+ if (inferred.length > 1) return void 0;
2962
+ const fallback = specificLineOfBusinessCodes(normalizeOperationalLinesOfBusiness(profileLinesOfBusiness));
2963
+ return fallback.length === 1 ? fallback[0] : void 0;
2964
+ }
2965
+ function annotateOperationalCoverageLinesOfBusiness(coverages, profileLinesOfBusiness) {
2966
+ return coverages.map((coverage) => {
2967
+ const lineOfBusiness = inferLineOfBusinessForOperationalCoverage(coverage, profileLinesOfBusiness);
2968
+ if (lineOfBusiness) return { ...coverage, lineOfBusiness };
2969
+ const next = { ...coverage };
2970
+ delete next.lineOfBusiness;
2971
+ return next;
2972
+ });
2973
+ }
2925
2974
  function inferLinesOfBusinessFromOperationalCoverages(coverages) {
2926
2975
  const inferred = [];
2927
2976
  for (const coverage of coverages) {
2928
- const limits = coverage.limits ?? [];
2929
- const text = [
2930
- coverage.coverageCode,
2931
- coverage.formNumber,
2932
- coverage.name,
2933
- ...limits.flatMap((term) => [term.appliesTo, term.label])
2934
- ].filter((value) => typeof value === "string" && value.trim().length > 0);
2935
- for (const value of text) {
2936
- for (const code of linesOfBusinessFromText(value)) {
2937
- if (!inferred.includes(code)) inferred.push(code);
2938
- }
2977
+ const explicit = explicitCoverageLineOfBusiness(coverage);
2978
+ if (explicit && !inferred.includes(explicit)) inferred.push(explicit);
2979
+ for (const code of coverageLineOfBusinessCandidates(coverage)) {
2980
+ if (!inferred.includes(code)) inferred.push(code);
2939
2981
  }
2940
2982
  }
2941
2983
  return inferred.slice(0, 6);
@@ -4191,9 +4233,42 @@ function cleanValue(value) {
4191
4233
  if (!value) return void 0;
4192
4234
  return normalizeWhitespace4(value.replace(/^[\s:;#-]+|[\s;,.]+$/g, ""));
4193
4235
  }
4236
+ function cleanCoverageLineOfBusiness(value) {
4237
+ if (typeof value !== "string" || !value.trim()) return void 0;
4238
+ const [code] = normalizeOperationalLinesOfBusiness([value]);
4239
+ return code && code !== "UN" ? code : void 0;
4240
+ }
4194
4241
  function normalizedFactValue(value) {
4195
4242
  return value.toLowerCase().replace(/&/g, " and ").replace(/[.,#]/g, " ").replace(/\s+/g, " ").trim();
4196
4243
  }
4244
+ var OPERATIONAL_ADDRESS_FIELDS = [
4245
+ "street1",
4246
+ "street2",
4247
+ "city",
4248
+ "state",
4249
+ "zip",
4250
+ "country",
4251
+ "formatted"
4252
+ ];
4253
+ function normalizeOperationalAddress(value) {
4254
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
4255
+ const record = value;
4256
+ const address = Object.fromEntries(
4257
+ OPERATIONAL_ADDRESS_FIELDS.flatMap((field) => {
4258
+ const cleaned = typeof record[field] === "string" ? cleanValue(record[field]) : void 0;
4259
+ return cleaned ? [[field, cleaned]] : [];
4260
+ })
4261
+ );
4262
+ return Object.keys(address).length > 0 ? address : void 0;
4263
+ }
4264
+ function normalizeOperationalPartyRole(value) {
4265
+ const normalized = value.toLowerCase().replace(/[\s-]+/g, "_");
4266
+ if (normalized === "namedinsured" || normalized === "insured") return "named_insured";
4267
+ if (normalized === "managing_general_agent") return "mga";
4268
+ if (normalized === "managing_general_underwriter") return "mga";
4269
+ if (normalized === "third_party_administrator") return "administrator";
4270
+ return normalized;
4271
+ }
4197
4272
  var OPERATIONAL_COVERAGE_TERM_KINDS = /* @__PURE__ */ new Set([
4198
4273
  "each_claim_limit",
4199
4274
  "each_occurrence_limit",
@@ -4292,6 +4367,7 @@ function mergeOperationalProfile(base, candidate, validNodeIds, validSpanIds) {
4292
4367
  const expirationDate = mergeValue(base.expirationDate, candidate.expirationDate);
4293
4368
  const retroactiveDate = mergeValue(base.retroactiveDate, candidate.retroactiveDate);
4294
4369
  const premium = mergeValue(base.premium, candidate.premium);
4370
+ const operationsDescription = mergeValue(base.operationsDescription, candidate.operationsDescription);
4295
4371
  const candidateDeclarationFacts = Array.isArray(candidate.declarationFacts) ? candidate.declarationFacts.map(mergeDeclarationFact).filter((fact) => Boolean(fact)) : [];
4296
4372
  const declarationFacts = [
4297
4373
  ...base.declarationFacts,
@@ -4316,7 +4392,8 @@ function mergeOperationalProfile(base, candidate, validNodeIds, validSpanIds) {
4316
4392
  effectiveDate,
4317
4393
  expirationDate,
4318
4394
  retroactiveDate,
4319
- premium
4395
+ premium,
4396
+ operationsDescription
4320
4397
  ].filter((value) => Boolean(value));
4321
4398
  const coverages = base.coverages.length > 0 ? base.coverages : Array.isArray(candidate.coverages) ? candidate.coverages.map((coverage) => {
4322
4399
  const record = coverage;
@@ -4350,6 +4427,7 @@ function mergeOperationalProfile(base, candidate, validNodeIds, validSpanIds) {
4350
4427
  if (!name || sourceNodeIds2.length === 0 && sourceSpanIds2.length === 0) return void 0;
4351
4428
  return {
4352
4429
  name,
4430
+ lineOfBusiness: cleanCoverageLineOfBusiness(record.lineOfBusiness),
4353
4431
  coverageCode: typeof record.coverageCode === "string" ? cleanValue(record.coverageCode) : void 0,
4354
4432
  limit: typeof record.limit === "string" ? cleanValue(record.limit) : void 0,
4355
4433
  deductible: typeof record.deductible === "string" ? cleanValue(record.deductible) : void 0,
@@ -4374,14 +4452,21 @@ function mergeOperationalProfile(base, candidate, validNodeIds, validSpanIds) {
4374
4452
  const record = party;
4375
4453
  const role = typeof record.role === "string" ? cleanValue(record.role) : void 0;
4376
4454
  const name = typeof record.name === "string" ? cleanValue(record.name) : void 0;
4455
+ const address = normalizeOperationalAddress(record.address);
4377
4456
  const sourceNodeIds2 = keepIds(record.sourceNodeIds, validNodeIds);
4378
4457
  const sourceSpanIds2 = keepIds(record.sourceSpanIds, validSpanIds);
4379
4458
  if (!role || !name || sourceNodeIds2.length === 0 && sourceSpanIds2.length === 0) return [];
4380
- return [{ role, name, sourceNodeIds: sourceNodeIds2, sourceSpanIds: sourceSpanIds2 }];
4459
+ return [{
4460
+ role: normalizeOperationalPartyRole(role),
4461
+ name,
4462
+ address,
4463
+ sourceNodeIds: sourceNodeIds2,
4464
+ sourceSpanIds: sourceSpanIds2
4465
+ }];
4381
4466
  }) : [];
4382
4467
  const parties = [
4383
- ...base.parties,
4384
4468
  ...candidateParties,
4469
+ ...base.parties,
4385
4470
  sourceBackedParty("named_insured", namedInsured),
4386
4471
  sourceBackedParty("insurer", insurer),
4387
4472
  sourceBackedParty("broker", broker)
@@ -4439,6 +4524,10 @@ function mergeOperationalProfile(base, candidate, validNodeIds, validSpanIds) {
4439
4524
  existingLinesOfBusiness: baseLinesOfBusiness,
4440
4525
  coverages
4441
4526
  });
4527
+ const annotatedCoverages = annotateOperationalCoverageLinesOfBusiness(
4528
+ coverages,
4529
+ resolvedLinesOfBusiness.linesOfBusiness
4530
+ );
4442
4531
  return PolicyOperationalProfileSchema.parse({
4443
4532
  ...base,
4444
4533
  documentType: candidate.documentType === "policy" ? "policy" : base.documentType,
@@ -4451,8 +4540,9 @@ function mergeOperationalProfile(base, candidate, validNodeIds, validSpanIds) {
4451
4540
  expirationDate,
4452
4541
  retroactiveDate,
4453
4542
  premium,
4543
+ operationsDescription,
4454
4544
  declarationFacts,
4455
- coverages,
4545
+ coverages: annotatedCoverages,
4456
4546
  parties,
4457
4547
  endorsementSupport,
4458
4548
  sourceNodeIds,
@@ -4482,6 +4572,7 @@ var OperationalProfileCleanupSchema = import_zod22.z.object({
4482
4572
  action: import_zod22.z.enum(["keep", "drop", "update"]),
4483
4573
  reason: import_zod22.z.string().optional(),
4484
4574
  name: import_zod22.z.string().optional(),
4575
+ lineOfBusiness: import_zod22.z.string().nullable().optional(),
4485
4576
  limit: import_zod22.z.string().nullable().optional(),
4486
4577
  deductible: import_zod22.z.string().nullable().optional(),
4487
4578
  premium: import_zod22.z.string().nullable().optional(),
@@ -4526,6 +4617,7 @@ function compactCoverageForCleanup(coverage, coverageIndex) {
4526
4617
  return {
4527
4618
  coverageIndex,
4528
4619
  name: coverage.name,
4620
+ lineOfBusiness: coverage.lineOfBusiness,
4529
4621
  limit: coverage.limit,
4530
4622
  deductible: coverage.deductible,
4531
4623
  premium: coverage.premium,
@@ -4555,6 +4647,7 @@ function nodeTextForSelection(node) {
4555
4647
  function coverageTextForSelection(coverage) {
4556
4648
  return [
4557
4649
  coverage.name,
4650
+ coverage.lineOfBusiness,
4558
4651
  coverage.coverageCode,
4559
4652
  coverage.limit,
4560
4653
  coverage.deductible,
@@ -4848,6 +4941,15 @@ function applyCoverageCleanupDecision(coverage, decision, validNodeIds, validSpa
4848
4941
  };
4849
4942
  const name = cleanProfileValue(decision.name);
4850
4943
  if (name) next.name = name;
4944
+ if (decision.lineOfBusiness != null) {
4945
+ const value = cleanProfileValue(decision.lineOfBusiness);
4946
+ if (value) {
4947
+ const parsed = AcordLobCodeSchema.safeParse(value);
4948
+ if (parsed.success && parsed.data !== "UN") next.lineOfBusiness = parsed.data;
4949
+ } else {
4950
+ delete next.lineOfBusiness;
4951
+ }
4952
+ }
4851
4953
  if (decision.limit != null) {
4852
4954
  const value = cleanProfileValue(decision.limit);
4853
4955
  if (value) next.limit = value;
@@ -4912,7 +5014,8 @@ function sourceIdsFromOperationalProfile(profile) {
4912
5014
  profile.effectiveDate,
4913
5015
  profile.expirationDate,
4914
5016
  profile.retroactiveDate,
4915
- profile.premium
5017
+ profile.premium,
5018
+ profile.operationsDescription
4916
5019
  ].filter(Boolean);
4917
5020
  return {
4918
5021
  sourceNodeIds: uniqueStrings([
@@ -4996,6 +5099,21 @@ var OperationalDeclarationFactForPromptSchema = import_zod23.z.object({
4996
5099
  sourceNodeIds: import_zod23.z.array(import_zod23.z.string()),
4997
5100
  sourceSpanIds: import_zod23.z.array(import_zod23.z.string())
4998
5101
  });
5102
+ var OperationalPartyForPromptSchema = import_zod23.z.object({
5103
+ role: import_zod23.z.enum([
5104
+ "named_insured",
5105
+ "producer",
5106
+ "broker",
5107
+ "insurer",
5108
+ "carrier",
5109
+ "mga",
5110
+ "administrator"
5111
+ ]),
5112
+ name: import_zod23.z.string(),
5113
+ address: OperationalAddressForPromptSchema.optional(),
5114
+ sourceNodeIds: import_zod23.z.array(import_zod23.z.string()),
5115
+ sourceSpanIds: import_zod23.z.array(import_zod23.z.string())
5116
+ });
4999
5117
  var OperationalProfilePromptSchema = import_zod23.z.object({
5000
5118
  documentType: import_zod23.z.enum(["policy", "quote"]).optional(),
5001
5119
  linesOfBusiness: import_zod23.z.array(import_zod23.z.string()).optional(),
@@ -5007,9 +5125,12 @@ var OperationalProfilePromptSchema = import_zod23.z.object({
5007
5125
  expirationDate: SourceBackedValueForPromptSchema.optional(),
5008
5126
  retroactiveDate: SourceBackedValueForPromptSchema.optional(),
5009
5127
  premium: SourceBackedValueForPromptSchema.optional(),
5128
+ operationsDescription: SourceBackedValueForPromptSchema.optional(),
5010
5129
  declarationFacts: import_zod23.z.array(OperationalDeclarationFactForPromptSchema).optional(),
5130
+ parties: import_zod23.z.array(OperationalPartyForPromptSchema).optional(),
5011
5131
  coverages: import_zod23.z.array(import_zod23.z.object({
5012
5132
  name: import_zod23.z.string(),
5133
+ lineOfBusiness: import_zod23.z.string().optional(),
5013
5134
  coverageCode: import_zod23.z.string().optional(),
5014
5135
  limit: import_zod23.z.string().optional(),
5015
5136
  deductible: import_zod23.z.string().optional(),
@@ -5918,6 +6039,9 @@ function operationalEvidenceScore(span) {
5918
6039
  if (span.metadata?.elementType === "title") score += 4;
5919
6040
  if (span.sourceUnit === "page") score -= 3;
5920
6041
  if (/\b(policy\s*(number|period|term)|effective date|expiration date|expiry date|named insured|insurer|carrier|security|broker|producer|premium|total due)\b/i.test(text)) score += 12;
6042
+ if (/\b(mailing address|business address|address|managing general (?:agent|underwriter)|mga|administrator|description of operations|operations description|nature of business|business description)\b/i.test(text)) score += 12;
6043
+ if (/\b\d{1,6}\s+[A-Z0-9][^\n,]{1,80}\b(?:street|st|avenue|ave|road|rd|boulevard|blvd|drive|dr|lane|ln|way|court|ct|highway|hwy)\b/i.test(text)) score += 10;
6044
+ if (/\b[A-Za-z][A-Za-z .'-]+,\s*[A-Z]{2}\s+\d{5}(?:-\d{4})?\b/.test(text)) score += 10;
5921
6045
  if (/\b(coverage part|limit(?:s)? of liability|deductible|retention|retroactive date|aggregate|sublimit|sub-limit|each claim|each loss|each occurrence|coinsurance)\b/i.test(text)) score += 14;
5922
6046
  if (/\bendorsement\s+(?:no\.?|number|#)?\s*[A-Z0-9]|forms? and endorsements?|attached at inception|schedule\b/i.test(text)) score += 8;
5923
6047
  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)) score += 10;
@@ -5937,10 +6061,13 @@ function isOperationalEvidenceAnchor(span) {
5937
6061
  const text = cleanText([span.text, span.formNumber].filter(Boolean).join(" "), "");
5938
6062
  if (!text) return false;
5939
6063
  if (sourceUnit3 === "page") {
5940
- return hasSubstantiveDeclarationsScheduleText(text) || /\b(declarations?|named insured|policy number|policy period|effective date|expiration date|premium|total due|producer)\b/i.test(text);
6064
+ return hasSubstantiveDeclarationsScheduleText(text) || /\b(declarations?|named insured|policy number|policy period|effective date|expiration date|premium|total due|producer|mailing address|mga|administrator|description of operations|nature of business|business description)\b/i.test(text);
5941
6065
  }
5942
6066
  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;
5943
6067
  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;
6068
+ if (/\b(mailing address|business address|address|managing general (?:agent|underwriter)|mga|administrator|description of operations|operations description|nature of business|business description)\b/i.test(text)) return true;
6069
+ if (/\b\d{1,6}\s+[A-Z0-9][^\n,]{1,80}\b(?:street|st|avenue|ave|road|rd|boulevard|blvd|drive|dr|lane|ln|way|court|ct|highway|hwy)\b/i.test(text)) return true;
6070
+ if (/\b[A-Za-z][A-Za-z .'-]+,\s*[A-Z]{2}\s+\d{5}(?:-\d{4})?\b/.test(text)) return true;
5944
6071
  if (/\b(coverage part|forms? and endorsements?|attached at inception|endorsement\s+(?:no\.?|number|#)?\s*[A-Z0-9])\b/i.test(text)) return true;
5945
6072
  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;
5946
6073
  return false;
@@ -5958,6 +6085,7 @@ function operationalEvidencePages(sourceSpans) {
5958
6085
  if (/\bcoverage parts?,?\s+limits? of liability,?\s+deductibles?,?\s+and retroactive dates\b/i.test(text)) score += 24;
5959
6086
  if (/\b(policy\s*(number|period|term)|effective date|expiration date|expiry date|named insured|insurer|carrier|security)\b/i.test(text)) score += 12;
5960
6087
  if (/\b(premium|total due|tax|fee|producer|broker)\b/i.test(text)) score += 10;
6088
+ if (/\b(mailing address|business address|managing general (?:agent|underwriter)|mga|administrator|description of operations|operations description|nature of business|business description)\b/i.test(text)) score += 16;
5961
6089
  if (/\b(endorsement\s+(?:no\.?|number|#)?\s*[A-Z0-9]|attached at inception|forms? and endorsements?)\b/i.test(text)) score += 8;
5962
6090
  if (/\b(definitions?|exclusions?|conditions?|duties in the event|action against|cancellation by)\b/i.test(text)) score -= 12;
5963
6091
  if (score > 0) scores.set(page, (scores.get(page) ?? 0) + score);
@@ -6032,7 +6160,7 @@ function operationalProfilePromptNodes(sourceTree) {
6032
6160
  return true;
6033
6161
  }
6034
6162
  const text = [node.title, node.path, node.description, node.textExcerpt].filter(Boolean).join(" ");
6035
- return /\b(policy\s*(number|period)|named insured|insurer|carrier|broker|producer|premium|coverage|limit|liability|deductible|retention|retroactive|aggregate|sublimit|sub-limit|endorsement)\b/i.test(text);
6163
+ return /\b(policy\s*(number|period)|named insured|insurer|carrier|broker|producer|managing general (?:agent|underwriter)|mga|administrator|mailing address|description of operations|operations description|nature of business|business description|premium|coverage|limit|liability|deductible|retention|retroactive|aggregate|sublimit|sub-limit|endorsement)\b/i.test(text);
6036
6164
  }).slice(0, 420);
6037
6165
  }
6038
6166
  function emptyOperationalProfile() {
@@ -6056,10 +6184,12 @@ function buildOperationalProfilePrompt(sourceTree, sourceSpans) {
6056
6184
  return `Extract a source-backed operational profile for an insurance policy.
6057
6185
 
6058
6186
  Return only high-value operational facts needed for policy lists, Q&A, compliance, and certificate generation:
6059
- - policy number, named insured, insurer/carrier/security, broker/producer, policy period, retroactive date, premium
6187
+ - policy number, named insured, insurer/carrier/security, broker/producer, policy period, retroactive date, premium, and the insured's description of operations
6188
+ - parties[] rows for named insured, producer/broker, insurer/carrier, and MGA/administrator identities and their mailing addresses
6060
6189
  - source-backed declarationFacts for named-insured identity details: named insured, mailing address, DBA, entity type, tax ID/FEIN, additional named insureds, and other durable declaration-page identity facts
6061
6190
  - coverage units with their own nested limit terms, deductibles/retentions, retroactive dates, premiums, and form references
6062
6191
  - coverage type labels
6192
+ - coverage lineOfBusiness ACORD codes when a coverage unit can be assigned to one specific line
6063
6193
 
6064
6194
  Rules:
6065
6195
  - Every returned value must include sourceNodeIds or sourceSpanIds from the provided evidence.
@@ -6067,12 +6197,17 @@ Rules:
6067
6197
  - If a value is not directly supported, omit it.
6068
6198
  - Prefer declarations, schedules, premium tables, and endorsement schedules over generic policy wording.
6069
6199
  - Put named-insured mailing address in declarationFacts[] with field "mailingAddress", valueKind "address", a user-facing value string, and structured address fields when present. Do not put producer, broker, insurer, mortgagee, loss-payee, or certificate-holder addresses in mailingAddress.
6200
+ - Put every source-backed policy party in parties[] using only these canonical roles: "named_insured", "producer", "broker", "insurer", "carrier", "mga", or "administrator". Keep producer/broker, insurer/carrier, and MGA/administrator addresses policy-scoped in parties[]; never represent them as the named-insured mailingAddress declaration fact.
6201
+ - When a party's address is present, return its available street1, street2, city, state, zip, country, and formatted parts under parties[].address. Partial addresses are allowed in parties[], but never guess a missing component. The party row must cite the source node or source span that supports both the identity and address.
6202
+ - Put the insured's directly stated business or operations description in operationsDescription. Do not synthesize it from coverages, industry inference, website research, or generic policy wording.
6070
6203
  - Put DBA or trade name in declarationFacts[] with field "dba"; entity/legal form in field "entityType"; FEIN/tax ID in field "taxId"; each additional named insured in field "additionalNamedInsured".
6071
6204
  - 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".
6072
6205
  - 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.
6073
6206
  - 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.
6074
6207
  - 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.
6075
6208
  - 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.
6209
+ - Put the ACORD line of business code for each coverage unit in coverages[].lineOfBusiness only when the row belongs to one specific line, such as CGL, AUTOB, WORK, UMBRC, EXLIA, EO, OLIB, EPLI, DO, FIDUC, CRIME, INMRC, COMAR, PROPC, PROP, BOP, HOME, DFIRE, FLOOD, or GARAG. Do not use limit labels such as Each Occurrence, Aggregate, Products-Completed Operations Aggregate, deductible, retention, retroactive date, or sublimit as lines of business.
6210
+ - If a package or multi-line row cannot be assigned to exactly one ACORD line, omit coverages[].lineOfBusiness for that coverage.
6076
6211
  - 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.
6077
6212
  - 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.
6078
6213
  - LiteParse text can fragment visual table cells into adjacent lines. Before extracting coverage terms, mentally join adjacent lines in the same declaration item or schedule row. For example, "$2,000,000 Policy Each Claim" followed immediately by "Aggregate" means "$2,000,000 Policy Aggregate"; a line ending with "/" followed by "Aggregate ..." means the limit cell continues, not a new coverage.
@@ -6134,13 +6269,6 @@ function valueOf(profile, key) {
6134
6269
  }
6135
6270
  return String(value.value);
6136
6271
  }
6137
- function provenanceOf(value) {
6138
- if (!value?.sourceSpanIds.length) return void 0;
6139
- return {
6140
- sourceSpanIds: value.sourceSpanIds,
6141
- ...value.sourceNodeIds[0] ? { documentNodeId: value.sourceNodeIds[0] } : {}
6142
- };
6143
- }
6144
6272
  function provenanceFromIds(sourceSpanIds, sourceNodeIds) {
6145
6273
  if (sourceSpanIds.length === 0) return void 0;
6146
6274
  return {
@@ -6148,6 +6276,33 @@ function provenanceFromIds(sourceSpanIds, sourceNodeIds) {
6148
6276
  ...sourceNodeIds[0] ? { documentNodeId: sourceNodeIds[0] } : {}
6149
6277
  };
6150
6278
  }
6279
+ function combinedProvenance(...values) {
6280
+ const sourceSpanIds = [...new Set(values.flatMap((value) => value?.sourceSpanIds ?? []))];
6281
+ const sourceNodeIds = [...new Set(values.flatMap((value) => value?.sourceNodeIds ?? []))];
6282
+ return provenanceFromIds(sourceSpanIds, sourceNodeIds);
6283
+ }
6284
+ function firstOperationalParty(profile, roles) {
6285
+ const candidates = roles.flatMap(
6286
+ (role) => profile.parties.filter((candidate) => candidate.role === role && candidate.name.trim())
6287
+ );
6288
+ return candidates.find((candidate) => candidate.address) ?? candidates[0];
6289
+ }
6290
+ function completeOperationalAddress(address) {
6291
+ if (!address?.street1 || !address.city || !address.state || !address.zip) return void 0;
6292
+ return {
6293
+ street1: address.street1,
6294
+ street2: address.street2,
6295
+ city: address.city,
6296
+ state: address.state,
6297
+ zip: address.zip,
6298
+ country: address.country
6299
+ };
6300
+ }
6301
+ function sourceBackedAddressFromParty(party) {
6302
+ const address = completeOperationalAddress(party?.address);
6303
+ const provenance = party ? provenanceFromIds(party.sourceSpanIds, party.sourceNodeIds) : void 0;
6304
+ return address && provenance ? { ...address, ...provenance } : void 0;
6305
+ }
6151
6306
  function declarationFactsByField(profile, field) {
6152
6307
  return profile.declarationFacts.filter((fact) => fact.field === field && fact.value.trim());
6153
6308
  }
@@ -6198,16 +6353,21 @@ function declarationFieldName(fact) {
6198
6353
  function materializeDocument(params) {
6199
6354
  const profile = params.operationalProfile;
6200
6355
  const policyNumber = valueOf(profile, "policyNumber") ?? "Unknown";
6201
- const insuredName = valueOf(profile, "namedInsured") ?? "Unknown";
6202
- const carrier = valueOf(profile, "insurer") ?? "Unknown";
6356
+ const namedInsuredParty = firstOperationalParty(profile, ["named_insured"]);
6357
+ const insurerParty = firstOperationalParty(profile, ["insurer", "carrier"]);
6358
+ const producerParty = firstOperationalParty(profile, ["producer", "broker"]);
6359
+ const insuredName = valueOf(profile, "namedInsured") ?? namedInsuredParty?.name ?? "Unknown";
6360
+ const carrier = valueOf(profile, "insurer") ?? insurerParty?.name ?? "Unknown";
6203
6361
  const effectiveDate = valueOf(profile, "effectiveDate") ?? "Unknown";
6204
6362
  const expirationDate = valueOf(profile, "expirationDate") ?? "Unknown";
6205
6363
  const premium = valueOf(profile, "premium");
6206
- const insurerProvenance = provenanceOf(profile.insurer);
6207
- const broker = valueOf(profile, "broker");
6208
- const brokerProvenance = provenanceOf(profile.broker);
6364
+ const insurerProvenance = combinedProvenance(profile.insurer, insurerParty);
6365
+ const insurerAddress = completeOperationalAddress(insurerParty?.address);
6366
+ const broker = valueOf(profile, "broker") ?? producerParty?.name;
6367
+ const brokerProvenance = combinedProvenance(profile.broker, producerParty);
6368
+ const producerAddress = completeOperationalAddress(producerParty?.address);
6209
6369
  const mailingAddressFact = firstDeclarationFact(profile, "mailingAddress");
6210
- const insuredAddress = sourceBackedAddressFromFact(mailingAddressFact);
6370
+ const insuredAddress = sourceBackedAddressFromParty(namedInsuredParty) ?? sourceBackedAddressFromFact(mailingAddressFact);
6211
6371
  const insuredDba = firstDeclarationFact(profile, "dba")?.value;
6212
6372
  const insuredEntityType = normalizedEntityType(firstDeclarationFact(profile, "entityType")?.value);
6213
6373
  const insuredFein = firstDeclarationFact(profile, "taxId")?.value;
@@ -6217,6 +6377,7 @@ function materializeDocument(params) {
6217
6377
  });
6218
6378
  const coverages = profile.coverages.map((coverage) => ({
6219
6379
  name: coverage.name,
6380
+ lineOfBusiness: coverage.lineOfBusiness,
6220
6381
  coverageCode: coverage.coverageCode,
6221
6382
  limit: coverage.limit,
6222
6383
  deductible: coverage.deductible,
@@ -6270,10 +6431,20 @@ function materializeDocument(params) {
6270
6431
  insuredEntityType,
6271
6432
  insuredFein,
6272
6433
  ...additionalNamedInsureds.length > 0 ? { additionalNamedInsureds } : {},
6273
- ...insurerProvenance ? { insurer: { legalName: carrier, ...insurerProvenance } } : {},
6434
+ ...insurerProvenance ? {
6435
+ insurer: {
6436
+ legalName: carrier,
6437
+ ...insurerAddress ? { address: insurerAddress } : {},
6438
+ ...insurerProvenance
6439
+ }
6440
+ } : {},
6274
6441
  ...broker && brokerProvenance ? {
6275
6442
  brokerAgency: broker,
6276
- producer: { agencyName: broker, ...brokerProvenance }
6443
+ producer: {
6444
+ agencyName: broker,
6445
+ ...producerAddress ? { address: producerAddress } : {},
6446
+ ...brokerProvenance
6447
+ }
6277
6448
  } : {},
6278
6449
  linesOfBusiness: profile.linesOfBusiness,
6279
6450
  formInventory: params.formInventory.filter((form) => typeof form.formNumber === "string" && form.formNumber.trim().length > 0).map((form) => ({
@@ -13720,6 +13891,7 @@ function getTemplate(policyType) {
13720
13891
  VerifyResultSchema,
13721
13892
  WatercraftDeclarationsSchema,
13722
13893
  WorkersCompDeclarationsSchema,
13894
+ annotateOperationalCoverageLinesOfBusiness,
13723
13895
  applyApplicationAnswers,
13724
13896
  buildAcroFormMappingPrompt,
13725
13897
  buildAgentSystemPrompt,
@@ -13786,6 +13958,7 @@ function getTemplate(policyType) {
13786
13958
  getNextApplicationQuestions,
13787
13959
  getPdfPageCount,
13788
13960
  getTemplate,
13961
+ inferLineOfBusinessForOperationalCoverage,
13789
13962
  inferLinesOfBusinessFromOperationalCoverages,
13790
13963
  isDoclingExtractionInput,
13791
13964
  isFileReference,