@claritylabs/cl-sdk 3.1.6 → 3.1.8

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
@@ -283,7 +283,6 @@ __export(src_exports, {
283
283
  buildConfirmationSummaryPrompt: () => buildConfirmationSummaryPrompt,
284
284
  buildConversationMemoryGuidance: () => buildConversationMemoryGuidance,
285
285
  buildCoverageGapPrompt: () => buildCoverageGapPrompt,
286
- buildDeterministicOperationalProfile: () => buildDeterministicOperationalProfile,
287
286
  buildDoclingProviderOptions: () => buildDoclingProviderOptions,
288
287
  buildDocumentSourceTree: () => buildDocumentSourceTree,
289
288
  buildFieldExplanationPrompt: () => buildFieldExplanationPrompt,
@@ -340,7 +339,6 @@ __export(src_exports, {
340
339
  getTemplate: () => getTemplate,
341
340
  isDoclingExtractionInput: () => isDoclingExtractionInput,
342
341
  isFileReference: () => isFileReference,
343
- mergeOperationalProfile: () => mergeOperationalProfile,
344
342
  mergeQuestionAnswers: () => mergeQuestionAnswers,
345
343
  mergeSourceSpans: () => mergeSourceSpans,
346
344
  normalizeApplicationQuestionGraph: () => normalizeApplicationQuestionGraph,
@@ -3364,878 +3362,6 @@ function buildDocumentSourceTree(sourceSpans, documentId) {
3364
3362
  return normalizeDocumentSourceTreePaths(groupPageContentByTitles([...nodes.values()]));
3365
3363
  }
3366
3364
 
3367
- // src/source/operational-profile.ts
3368
- function normalizeWhitespace3(value) {
3369
- return value.replace(/\s+/g, " ").trim();
3370
- }
3371
- function cleanValue(value) {
3372
- if (!value) return void 0;
3373
- return normalizeWhitespace3(value.replace(/^[\s:;#-]+|[\s;,.]+$/g, ""));
3374
- }
3375
- function cleanCoverageLabel(value) {
3376
- return cleanValue(value)?.replace(/^column\s+\d+\s*:\s*/i, "");
3377
- }
3378
- function moneyValue(value) {
3379
- const clean = cleanValue(value);
3380
- if (!clean) return void 0;
3381
- if (/^\$/.test(clean)) return clean;
3382
- if (/^\d{1,3}(?:,\d{3})*(?:\.\d{2})?$/.test(clean)) return `$${clean}`;
3383
- return clean;
3384
- }
3385
- function premiumValue(value) {
3386
- const clean = cleanValue(value);
3387
- if (!clean) return void 0;
3388
- if (/\$[A-Z0-9]/i.test(clean)) return clean;
3389
- if (/\b(?:CAD|USD)\b/i.test(clean) && /(?:\d|X{2,})/i.test(clean)) return clean;
3390
- if (/^\d{1,3}(?:,\d{3})+\.\d{2}$/.test(clean) || /^\d+\.\d{2}$/.test(clean)) return `$${clean}`;
3391
- if (/^X{2,}(?:,X{3})*(?:\.X{2})$/i.test(clean)) return clean;
3392
- return void 0;
3393
- }
3394
- function nodeText(node) {
3395
- return normalizeWhitespace3([
3396
- node.title,
3397
- node.description,
3398
- node.textExcerpt
3399
- ].filter(Boolean).join(" "));
3400
- }
3401
- function valueFromNode(node, value, confidence = "medium") {
3402
- return {
3403
- value,
3404
- confidence,
3405
- sourceNodeIds: [node.id],
3406
- sourceSpanIds: node.sourceSpanIds
3407
- };
3408
- }
3409
- function valueFromNodes(nodes, value, confidence = "medium", normalizedValue) {
3410
- return {
3411
- value,
3412
- ...normalizedValue ? { normalizedValue } : {},
3413
- confidence,
3414
- ...sourceIds(nodes)
3415
- };
3416
- }
3417
- function firstMatch(nodes, patterns) {
3418
- for (const node of nodes) {
3419
- const text = nodeText(node);
3420
- for (const pattern of patterns) {
3421
- const match = text.match(pattern);
3422
- const value = cleanValue(match?.[1]);
3423
- if (value) return valueFromNode(node, value, "high");
3424
- }
3425
- }
3426
- return void 0;
3427
- }
3428
- var POLICY_NUMBER_PATTERNS = [
3429
- /\bpolicy\s*(?:number|no\.?|#)\s*:?\s*([A-Z0-9][A-Z0-9,.-]{4,}[A-Z0-9])/i,
3430
- /\bpolicy\s*[:#]\s*([A-Z0-9][A-Z0-9,.-]{4,}[A-Z0-9])/i
3431
- ];
3432
- function policyNumberEvidenceScore(node) {
3433
- const text = normalizeWhitespace3([node.path, nodeText(node)].filter(Boolean).join(" ")).toLowerCase();
3434
- let score = 0;
3435
- if (/\b(policy\s+summary|declarations?|declaration\s+page|schedule)\b/.test(text)) score += 80;
3436
- if (/\b(plan|policy\s+date|insured\s+person|named\s+insured|insurance\s+amount|benefit\s+amount)\b/.test(text)) score += 35;
3437
- if (node.kind === "table_row" || node.kind === "table_cell" || node.kind === "text") score += 20;
3438
- if (node.kind === "page") score += 10;
3439
- if (typeof node.pageStart === "number" && node.pageStart > 1 && node.pageStart <= 10) score += 20;
3440
- if (typeof node.pageStart === "number" && node.pageStart === 1) score -= 30;
3441
- if (/\b(notices?\s+and\s+jacket|policy\s+jacket|front\s+matter|table\s+of\s+contents)\b/.test(text)) score -= 70;
3442
- if (node.kind === "page_group" || node.kind === "form") score -= 30;
3443
- return score;
3444
- }
3445
- function policyNumberFromNodes(nodes) {
3446
- const candidates = nodes.slice(0, 120).flatMap((node) => {
3447
- const text = nodeText(node);
3448
- for (const pattern of POLICY_NUMBER_PATTERNS) {
3449
- const value = cleanValue(text.match(pattern)?.[1]);
3450
- if (value) return [{ node, value, score: policyNumberEvidenceScore(node) }];
3451
- }
3452
- return [];
3453
- }).sort(
3454
- (left, right) => right.score - left.score || (left.node.pageStart ?? Number.MAX_SAFE_INTEGER) - (right.node.pageStart ?? Number.MAX_SAFE_INTEGER) || left.node.order - right.node.order
3455
- );
3456
- const candidate = candidates[0];
3457
- return candidate ? valueFromNode(candidate.node, candidate.value, "high") : void 0;
3458
- }
3459
- function compactFactNodes(nodes) {
3460
- return nodes.filter((node) => {
3461
- if (node.kind === "document" || node.kind === "page_group" || node.kind === "form") return false;
3462
- const text = nodeText(node);
3463
- if (text.length > 900) return false;
3464
- if (/\b(table of contents|provided solely for your convenience|not to be construed|actual policy issued)\b/i.test(text)) return false;
3465
- return node.kind === "text" || node.kind === "table_row" || node.kind === "table_cell" || node.kind === "page";
3466
- });
3467
- }
3468
- function firstCleanMatch(nodes, patterns, clean) {
3469
- for (const node of compactFactNodes(nodes)) {
3470
- const text = nodeText(node);
3471
- for (const pattern of patterns) {
3472
- const raw = cleanValue(text.match(pattern)?.[1]);
3473
- if (!raw) continue;
3474
- const value = clean(raw);
3475
- if (value) return valueFromNode(node, value, "high");
3476
- }
3477
- }
3478
- return void 0;
3479
- }
3480
- function cleanNamedInsured(value) {
3481
- const clean = cleanValue(value.replace(/\bborn\s+on\b.*$/i, "").replace(/\bage\s+nearest\b.*$/i, "").replace(/\bbeneficiary\b.*$/i, ""));
3482
- if (!clean || clean.length > 160) return void 0;
3483
- if (!/[A-Za-z0-9]/.test(clean)) return void 0;
3484
- if (/^(person|persons)\b/i.test(clean)) return void 0;
3485
- if (/^(insurance amount|benefit amount|policy number|policy date|owner|beneficiary|premium|coverage|risk classification)\b/i.test(clean)) {
3486
- return void 0;
3487
- }
3488
- if (/\b(table of contents|policy wording|provided solely|convenience|not to be construed|actual policy issued)\b/i.test(clean)) {
3489
- return void 0;
3490
- }
3491
- return clean;
3492
- }
3493
- var PARTY_LABEL_PATTERNS = {
3494
- namedInsured: /^(?:item\s*\d+[.)]?\s*)?(?:named insured(?:\s+and\s+address)?|insured name)$/i,
3495
- insurer: /^(?:carrier|insurer|security)$/i,
3496
- broker: /^(?:broker(?:\s+of\s+record)?|producer|agent)$/i
3497
- };
3498
- function partyLabelKind(value) {
3499
- const clean = cleanValue(value.replace(/^\s*column\s+\d+\s*:\s*/i, ""));
3500
- if (!clean) return void 0;
3501
- if (PARTY_LABEL_PATTERNS.namedInsured.test(clean)) return "namedInsured";
3502
- if (PARTY_LABEL_PATTERNS.insurer.test(clean)) return "insurer";
3503
- if (PARTY_LABEL_PATTERNS.broker.test(clean)) return "broker";
3504
- return void 0;
3505
- }
3506
- function isRejectedPartyValue(value) {
3507
- const clean = normalizeWhitespace3(value);
3508
- return /^(?:and address|of record|insurer|carrier|security|broker|producer|agent|the|a|an|is|are|was|were|agrees?|means?|includes?|shall|will)\b/i.test(clean);
3509
- }
3510
- function identityWithoutAddress(value) {
3511
- const clean = cleanValue(value.replace(/\b(?:phone|tel|telephone|email|e-mail)\b.*$/i, "").replace(/\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b.*$/i, ""));
3512
- if (!clean) return void 0;
3513
- const beforeStreet = clean.match(/^(.+?)\s+\d{1,6}\s+[A-Za-z0-9.'-]+(?:\s+[A-Za-z0-9.'-]+){0,5}\s+(?:street|st\.?|avenue|ave\.?|road|rd\.?|drive|dr\.?|lane|ln\.?|boulevard|blvd\.?|suite|ste\.?|floor|fl\.?|way|court|ct\.?)\b/i)?.[1];
3514
- const beforeCityState = clean.match(/^(.+?)\s+[A-Z][A-Za-z.'-]+(?:\s+[A-Z][A-Za-z.'-]+)*,\s*[A-Z]{2}\s+\d{5}(?:-\d{4})?\b/)?.[1];
3515
- return cleanValue(beforeStreet ?? beforeCityState ?? clean);
3516
- }
3517
- function cleanPartyIdentity(value, clean) {
3518
- const raw = cleanValue(value);
3519
- if (!raw || isRejectedPartyValue(raw)) return void 0;
3520
- const identity = identityWithoutAddress(raw);
3521
- const cleaned = identity ? clean(identity) : void 0;
3522
- if (!cleaned || isRejectedPartyValue(cleaned)) return void 0;
3523
- return {
3524
- value: cleaned,
3525
- ...cleaned !== raw ? { normalizedValue: cleaned } : {}
3526
- };
3527
- }
3528
- function partyFromTableRows(nodes, wanted, clean) {
3529
- const children = childMap(nodes);
3530
- for (const row of compactFactNodes(nodes).filter((node) => node.kind === "table_row")) {
3531
- const cells = cellRows(row, children);
3532
- for (const [index, cell] of cells.entries()) {
3533
- const labelKind = partyLabelKind(cell.value) ?? partyLabelKind(cell.label);
3534
- if (labelKind !== wanted) continue;
3535
- const valueCell = cells.slice(index + 1).find((candidate) => !partyLabelKind(candidate.value) && !partyLabelKind(candidate.label));
3536
- if (!valueCell) continue;
3537
- const cleaned = cleanPartyIdentity(valueCell.value, clean);
3538
- if (cleaned) return valueFromNodes([row, valueCell.node], cleaned.value, "high", cleaned.normalizedValue);
3539
- }
3540
- const parts = normalizeWhitespace3(row.textExcerpt ?? row.description ?? nodeText(row)).split(/\s+\|\s+|\t/).map(cleanValue).filter((part) => Boolean(part));
3541
- for (const [index, part] of parts.entries()) {
3542
- const labelKind = partyLabelKind(part);
3543
- if (labelKind !== wanted) continue;
3544
- const cleaned = cleanPartyIdentity(parts[index + 1] ?? "", clean);
3545
- if (cleaned) return valueFromNodes([row], cleaned.value, "high", cleaned.normalizedValue);
3546
- }
3547
- }
3548
- return void 0;
3549
- }
3550
- function namedInsuredFromNodes(nodes) {
3551
- return partyFromTableRows(nodes, "namedInsured", cleanNamedInsured) ?? firstCleanMatch(nodes, [
3552
- /\b(?:named insured|insured name)\s*:?\s*(.+?)(?=\s+(?:insurance amount|benefit amount|policy number|policy date|owner|beneficiary|premium|coverage|risk classification|date this)\b|[|;\n]|$)/i,
3553
- /\b(?:insured persons?|insured person)\s*:\s*(.+?)(?=\s+(?:insurance amount|benefit amount|policy number|policy date|owner|beneficiary|premium|coverage|risk classification|date this)\b|[|;\n]|$)/i,
3554
- /\b(?:applicant|policyholder)\s*:?\s*(.+?)(?=\s+(?:coverage|policy number|insurer|carrier|premium|effective|expiration)\b|[|;\n]|$)/i
3555
- ], cleanNamedInsured);
3556
- }
3557
- function cleanInsurer(value) {
3558
- const clean = cleanValue(value);
3559
- if (!clean || clean.length > 140) return void 0;
3560
- if (/^(mean|means|we|us|our|the|a|an|agrees?|shall|will)\b/i.test(clean)) return void 0;
3561
- if (/\b(table of contents|policy wording|provided solely|convenience)\b/i.test(clean)) return void 0;
3562
- const known = clean.match(/\b(Sun Life Assurance Company of Canada|Manulife|The Manufacturers Life Insurance Company)\b/i)?.[1];
3563
- return known ?? clean;
3564
- }
3565
- function insurerFromNodes(nodes) {
3566
- const tableValue = partyFromTableRows(nodes, "insurer", cleanInsurer);
3567
- if (tableValue) return tableValue;
3568
- for (const node of compactFactNodes(nodes)) {
3569
- const text = nodeText(node);
3570
- const value = cleanInsurer(text.match(/\b([A-Z][A-Za-z&.,' -]{2,120}?(?:Insurance|Assurance|Indemnity|Casualty|Underwriting|Mutual|Risk|Reinsurance)\s+Company(?:\s+of\s+[A-Z][A-Za-z .'-]+)?)\s*\(\s*the\s+["']?Insurer["']?\s*\)/i)?.[1] ?? "");
3571
- if (value) return valueFromNode(node, value, "high");
3572
- }
3573
- return firstCleanMatch(nodes, [
3574
- /\bunderwritten by\s+([^|;\n.]{3,160})/i,
3575
- /\b(?:insurer|carrier|company|security)\s*:?\s*([^|;\n]{3,120})/i,
3576
- /\b(Sun Life Assurance Company of Canada|Manulife|The Manufacturers Life Insurance Company)\b/i
3577
- ], cleanInsurer);
3578
- }
3579
- function inferPolicyTypes(nodes) {
3580
- const text = nodes.slice(0, 40).map(nodeText).join(" ").toLowerCase();
3581
- const types = [];
3582
- const add = (pattern, type) => {
3583
- if (pattern.test(text) && !types.includes(type)) types.push(type);
3584
- };
3585
- add(/\b(life insurance|permanent life|term life|whole life|universal life|sun permanent life|sun par protector|manulife par|vitality\s*plus|death benefit)\b/i, "life");
3586
- add(/\b(critical illness|critical illness insurance|covered critical illness|partial benefit payout)\b/i, "critical_illness");
3587
- add(/\b(disability benefit|total disability|catastrophic disability|disability waiver|waiver of premium disability)\b/i, "disability");
3588
- add(/\b(long[-\s]?term care|long term care conversion)\b/i, "long_term_care");
3589
- add(/\b(cyber|network security|privacy liability|data breach)\b/i, "cyber");
3590
- add(/\b(professional liability|errors?\s*&?\s*omissions|e&o)\b/i, "professional_liability");
3591
- add(/\b(commercial general liability|general liability|cgl)\b/i, "general_liability");
3592
- add(/\b(umbrella|excess liability)\b/i, "umbrella");
3593
- add(/\b(workers'? compensation|employers'? liability)\b/i, "workers_comp");
3594
- add(/\b(commercial auto|business auto|automobile liability)\b/i, "commercial_auto");
3595
- add(/\b(commercial property|property coverage|building coverage)\b/i, "commercial_property");
3596
- return types.length ? types : ["other"];
3597
- }
3598
- function inferDocumentType(nodes) {
3599
- const text = nodes.slice(0, 25).map(nodeText).join(" ").toLowerCase();
3600
- if (/\b(quote|proposal|quotation|indication)\b/.test(text) && !/\bpolicy number\b/.test(text)) {
3601
- return "quote";
3602
- }
3603
- return "policy";
3604
- }
3605
- function coverageNameFromRow(text) {
3606
- const labelled = text.match(/\b(coverage part|coverage|line)\s*:?\s*([^|;$]{3,80})/i);
3607
- if (labelled) {
3608
- const value = cleanCoverageLabel(labelled[2]);
3609
- if (!value) return void 0;
3610
- return /^coverage part$/i.test(labelled[1]) ? `Coverage Part ${value}` : value;
3611
- }
3612
- const parts = text.split(/\s+\|\s+| {2,}|\t/).map(cleanCoverageLabel).filter(Boolean);
3613
- const first = parts.find(
3614
- (part) => !/^(limit|limits?|deductible|premium|amount|basis|rate|retroactive|aggregate|each occurrence)$/i.test(part) && !/^\$?[\d,]+/.test(part)
3615
- );
3616
- return cleanCoverageLabel(first);
3617
- }
3618
- function isDeclarationLimitLabel(value) {
3619
- const label = normalizeLabel(value);
3620
- return /\bitem\s*\d+[.)]?\s*limits?\s+of\s+liability\b/.test(label) || /^limits?\s+of\s+liability$/.test(label) || /^policy\s+limits?$/.test(label);
3621
- }
3622
- function declarationCoverageNameFromRow(row, children) {
3623
- const cells = cellRows(row, children);
3624
- const candidates = [
3625
- row.title,
3626
- ...cells.flatMap((cell) => [cell.label, cell.value])
3627
- ];
3628
- if (!candidates.some(isDeclarationLimitLabel)) return void 0;
3629
- return candidates.some((candidate) => /\blimits?\s+of\s+liability\b/i.test(candidate ?? "")) ? "Limits of Liability" : "Policy Limits";
3630
- }
3631
- function limitFromText(text) {
3632
- return moneyValue(
3633
- text.match(/\b(?:limit|liability|aggregate|occurrence|claim)\s*:?\s*(\$?\d[\d,]*(?:\.\d{2})?|\$?\d[\d,]*\s*(?:each|per|aggregate)[^|;]*)/i)?.[1] ?? text.match(/(\$\s?\d[\d,]*(?:\.\d{2})?)(?=.*\b(limit|aggregate|occurrence|claim|liability)\b)/i)?.[1]
3634
- );
3635
- }
3636
- function deductibleFromText(text) {
3637
- return moneyValue(text.match(/\b(?:deductible|retention|sir)\s*:?\s*(\$?\d[\d,]*(?:\.\d{2})?)/i)?.[1]);
3638
- }
3639
- function premiumFromText(text) {
3640
- return premiumValue(text.match(/\b(?:premium|total premium|total cost|amount due)\b(?:\s*(?:is|:|-))?\s*(\$?(?:\d[\d,]*(?:\.\d{2})?|X{2,}(?:,X{3})*(?:\.X{2})?))/i)?.[1]);
3641
- }
3642
- function premiumFromNodes(nodes) {
3643
- for (const node of compactFactNodes(nodes)) {
3644
- const value = premiumFromText(nodeText(node));
3645
- if (value) return valueFromNode(node, value, "high");
3646
- }
3647
- return void 0;
3648
- }
3649
- function moneyAmount(value) {
3650
- const clean = cleanValue(value);
3651
- if (!clean) return void 0;
3652
- const currency = clean.match(/\$\s*([0-9][0-9,]*(?:\.\d+)?)/);
3653
- const percent = clean.match(/\b([0-9][0-9,]*(?:\.\d+)?)\s*%/);
3654
- const explicitNumeric = !/\bitem\s*\d+\b/i.test(clean) && /\b(?:limit|aggregate|claim|occurrence|loss|retention|deductible|sir|premium|amount)\b/i.test(clean) ? clean.match(/\b([0-9][0-9,]*(?:\.\d+)?)\b/) : void 0;
3655
- const match = currency ?? percent ?? explicitNumeric;
3656
- if (!match) return void 0;
3657
- const amount = Number(match[1].replace(/,/g, ""));
3658
- return Number.isFinite(amount) ? amount : void 0;
3659
- }
3660
- function normalizeLabel(value) {
3661
- return normalizeWhitespace3(value ?? "").toLowerCase();
3662
- }
3663
- function isGenericColumnLabel(value) {
3664
- return /^column\s+\d+$/i.test(cleanValue(value) ?? "");
3665
- }
3666
- function isHeaderRow(row) {
3667
- return row.metadata?.isHeader === true || row.metadata?.isHeader === "true";
3668
- }
3669
- var OPERATIONAL_COVERAGE_TERM_KINDS = /* @__PURE__ */ new Set([
3670
- "each_claim_limit",
3671
- "each_occurrence_limit",
3672
- "each_loss_limit",
3673
- "aggregate_limit",
3674
- "sublimit",
3675
- "retention",
3676
- "deductible",
3677
- "retroactive_date",
3678
- "premium",
3679
- "other"
3680
- ]);
3681
- function termKind(label, value) {
3682
- const text = normalizeLabel(`${label} ${value}`);
3683
- if (/\bretroactive\b/.test(text)) return "retroactive_date";
3684
- if (/\b(self[-\s]?insured retention|retention|sir)\b/.test(text)) return "retention";
3685
- if (/\bdeductible\b/.test(text)) return "deductible";
3686
- if (/\bpremium\b/.test(text)) return "premium";
3687
- if (/\bsub[-\s]?limit\b/.test(text)) return "sublimit";
3688
- if (/\baggregate\b/.test(text)) return "aggregate_limit";
3689
- if (/\beach\s+proceeding|per\s+proceeding\b/.test(text)) return "sublimit";
3690
- if (/\beach\s+claim|per\s+claim\b/.test(text)) return "each_claim_limit";
3691
- if (/\beach\s+occurrence|per\s+occurrence\b/.test(text)) return "each_occurrence_limit";
3692
- if (/\beach\s+loss|per\s+loss\b/.test(text)) return "each_loss_limit";
3693
- if (/\blimit\b/.test(text)) return "other";
3694
- return "other";
3695
- }
3696
- function normalizeTermKind(value, label, termValue) {
3697
- return typeof value === "string" && OPERATIONAL_COVERAGE_TERM_KINDS.has(value) ? value : termKind(label, termValue);
3698
- }
3699
- function isValueCell(label, value) {
3700
- const normalizedLabel = normalizeLabel(label);
3701
- const normalizedValue = normalizeLabel(value);
3702
- if (!value || normalizedValue === "\u2014") return false;
3703
- if (/\b(policy\s*(number|no|#)|named insured|insured|carrier|company|broker|producer|agent|coverage|coverage part|description|item|name|form|source)\b/.test(normalizedLabel)) {
3704
- return false;
3705
- }
3706
- return /\b(limit|aggregate|retention|deductible|sir|retroactive|premium|amount)\b/.test(normalizedLabel) || moneyAmount(value) !== void 0 || /\b(full prior acts|none|included|not included|as stated)\b/.test(normalizedValue);
3707
- }
3708
- function isCoverageTermLabel(value) {
3709
- const label = normalizeLabel(value);
3710
- return /\b(limit|aggregate|retention|deductible|sir|retroactive|premium|amount|sub[-\s]?limit)\b/.test(label);
3711
- }
3712
- function isNameCell(label, value) {
3713
- const normalizedLabel = normalizeLabel(label);
3714
- const normalizedValue = normalizeLabel(value);
3715
- if (!value || moneyAmount(value) !== void 0) return false;
3716
- if (/^item\s+\d+[.)]?\s*limits?\s+of\s+liability\b/.test(normalizedValue)) return false;
3717
- if (/\b(coverage|coverage part|insuring agreement|description|item|name)\b/.test(normalizedLabel)) {
3718
- return true;
3719
- }
3720
- if (/\b(sub[-\s]?limit|aggregate(?:\s+policy)?\s+limit|limit\s+of\s+liability)\b/.test(normalizedLabel) && /\b[a-z][a-z0-9&/ -]{2,}\b/.test(normalizedValue) && !/\bcoverage\s+part\s+[a-z]\)?$/.test(normalizedValue)) {
3721
- return true;
3722
- }
3723
- if (/^column\s+1$/.test(normalizedLabel) && !/^(item\s+\d+|nwc-|iso-|cg |il |form\b|page\b)/i.test(normalizedValue)) {
3724
- return true;
3725
- }
3726
- if (/^column\s+\d+$/.test(normalizedLabel) && /\b(coverage|sub[-\s]?limit|liability|expense|part\s+[a-z])\b/.test(normalizedValue)) {
3727
- return true;
3728
- }
3729
- return false;
3730
- }
3731
- function childMap(nodes) {
3732
- const children = /* @__PURE__ */ new Map();
3733
- for (const node of nodes) {
3734
- const group = children.get(node.parentId) ?? [];
3735
- group.push(node);
3736
- children.set(node.parentId, group);
3737
- }
3738
- for (const group of children.values()) group.sort((left, right) => left.order - right.order);
3739
- return children;
3740
- }
3741
- function rawCellRows(row, children) {
3742
- return (children.get(row.id) ?? []).filter((child) => child.kind === "table_cell").map((cell) => ({
3743
- label: cleanValue(cell.title) ?? "Value",
3744
- value: cleanValue(cell.textExcerpt ?? cell.description ?? cell.title) ?? "",
3745
- node: cell
3746
- })).filter((cell) => cell.value);
3747
- }
3748
- function headerLabelsForRow(row, children) {
3749
- if (!row.parentId) return [];
3750
- const labels = [];
3751
- const siblingRows = (children.get(row.parentId) ?? []).filter((candidate) => candidate.kind === "table_row" && candidate.order < row.order).sort((left, right) => left.order - right.order);
3752
- for (const sibling of siblingRows) {
3753
- if (!isHeaderRow(sibling)) continue;
3754
- for (const [index, cell] of rawCellRows(sibling, children).entries()) {
3755
- const label = cleanValue(cell.value) ?? cleanValue(cell.label);
3756
- if (label && !isGenericColumnLabel(label)) labels[index] = label;
3757
- }
3758
- }
3759
- return labels;
3760
- }
3761
- function cellRows(row, children, headerLabels = []) {
3762
- return rawCellRows(row, children).map((cell, index) => ({
3763
- ...cell,
3764
- label: isGenericColumnLabel(cell.label) && headerLabels[index] ? headerLabels[index] : cell.label
3765
- }));
3766
- }
3767
- function directChildren(parent, children) {
3768
- return children.get(parent.id) ?? [];
3769
- }
3770
- function descendants(parent, children) {
3771
- const stack = [...directChildren(parent, children)];
3772
- const result = [];
3773
- while (stack.length > 0) {
3774
- const node = stack.shift();
3775
- result.push(node);
3776
- stack.unshift(...directChildren(node, children));
3777
- }
3778
- return result;
3779
- }
3780
- function ancestry(node, byId) {
3781
- const result = [];
3782
- let current = node.parentId ? byId.get(node.parentId) : void 0;
3783
- while (current) {
3784
- result.push(current);
3785
- current = current.parentId ? byId.get(current.parentId) : void 0;
3786
- }
3787
- return result;
3788
- }
3789
- function endorsementAncestor(node, byId) {
3790
- return ancestry(node, byId).find((ancestor) => ancestor.kind === "endorsement");
3791
- }
3792
- function endorsementNumberFrom(value) {
3793
- return cleanValue(value?.match(/\bendorsement\s+no\.?\s*([0-9A-Z-]+)/i)?.[1]);
3794
- }
3795
- function endorsementNameFrom(node) {
3796
- const candidates = [node.title, node.textExcerpt, node.description, nodeText(node)].filter(Boolean);
3797
- for (const candidate of candidates) {
3798
- const match = candidate.match(/\bendorsement\s+no\.?\s*([0-9A-Z-]+)\s*[—-]\s*(.{3,140}?)(?=\s+This\s+endorsement\b|\.|$)/i);
3799
- if (match) return cleanValue(`Endorsement No. ${match[1]} - ${match[2]}`) ?? node.title;
3800
- }
3801
- return node.title;
3802
- }
3803
- function sourceIds(nodes) {
3804
- return {
3805
- sourceNodeIds: [...new Set(nodes.map((node) => node.id))],
3806
- sourceSpanIds: [...new Set(nodes.flatMap((node) => node.sourceSpanIds))]
3807
- };
3808
- }
3809
- function termFromCell(params) {
3810
- const value = cleanCoverageTermValue(params.value);
3811
- if (!value) return void 0;
3812
- if (isRejectedCoverageTermValue(params.label, value)) return void 0;
3813
- const nodes = [params.row, params.cell].filter((node) => Boolean(node));
3814
- const kind = termKind(params.label, value);
3815
- const amount = moneyAmount(value);
3816
- return {
3817
- kind,
3818
- label: params.label,
3819
- value,
3820
- ...amount !== void 0 && kind !== "retroactive_date" ? { amount } : {},
3821
- ...params.appliesTo ? { appliesTo: params.appliesTo } : {},
3822
- ...sourceIds(nodes)
3823
- };
3824
- }
3825
- function cleanCoverageTermValue(value) {
3826
- return cleanValue(value)?.replace(/\s+\/\s*$/, "").trim();
3827
- }
3828
- function isRejectedCoverageTermValue(label, value) {
3829
- if (/\bshown\s+in\s+item\s*\d+\b/i.test(value) && moneyAmount(value) === void 0) return true;
3830
- if (/\b(does not afford coverage|doesn't afford coverage|no coverage|remains excluded|is excluded|are excluded|shall not cover|will not cover)\b/i.test(value) && moneyAmount(value) === void 0) {
3831
- return true;
3832
- }
3833
- if (/^for:\s*\(\d+\)/i.test(label) && moneyAmount(value) === void 0) return true;
3834
- if (value.length > 80 && /\b(exclusion|excluded|shall not|will not|does not|failure to)\b/i.test(value) && moneyAmount(value) === void 0) return true;
3835
- return false;
3836
- }
3837
- function termsFromRow(row, children) {
3838
- const cells = cellRows(row, children, headerLabelsForRow(row, children));
3839
- if (cells.length > 0) {
3840
- const pairedTerms = [];
3841
- const pairedIndexes = /* @__PURE__ */ new Set();
3842
- for (const [index, cell] of cells.entries()) {
3843
- const next = cells[index + 1];
3844
- if (!next) continue;
3845
- if (!isGenericColumnLabel(cell.label) && isNameCell(cell.label, cell.value)) continue;
3846
- if (!isCoverageTermLabel(cell.value)) continue;
3847
- if (isCoverageTermLabel(next.value) && moneyAmount(next.value) === void 0) continue;
3848
- const term = termFromCell({ row, cell: next.node, label: cell.value, value: next.value });
3849
- if (term) {
3850
- pairedTerms.push(term);
3851
- pairedIndexes.add(index);
3852
- pairedIndexes.add(index + 1);
3853
- }
3854
- }
3855
- const valueTerms = cells.filter((_, index) => !pairedIndexes.has(index)).filter((cell) => isValueCell(cell.label, cell.value)).map((cell) => termFromCell({ row, cell: cell.node, label: cell.label, value: cell.value })).filter((term) => Boolean(term));
3856
- return [...pairedTerms, ...valueTerms];
3857
- }
3858
- const text = normalizeWhitespace3(row.textExcerpt ?? row.description ?? nodeText(row));
3859
- const terms = [];
3860
- for (const part of text.split(/\s+\|\s+/)) {
3861
- const match = part.match(/^([^:]{2,80}):\s*(.+)$/);
3862
- if (!match) continue;
3863
- if (!isValueCell(match[1], match[2])) continue;
3864
- const term = termFromCell({ row, label: cleanValue(match[1]) ?? "Value", value: match[2] });
3865
- if (term) terms.push(term);
3866
- }
3867
- if (terms.length === 0) {
3868
- const limit = limitFromText(text);
3869
- if (limit) {
3870
- const term = termFromCell({ row, label: "Limit", value: limit });
3871
- if (term) terms.push(term);
3872
- }
3873
- const deductible = deductibleFromText(text);
3874
- if (deductible) {
3875
- const term = termFromCell({ row, label: "Deductible", value: deductible });
3876
- if (term) terms.push(term);
3877
- }
3878
- }
3879
- return terms;
3880
- }
3881
- function nameFromRow(row, children) {
3882
- const cells = cellRows(row, children, headerLabelsForRow(row, children));
3883
- const named = cells.find((cell) => isNameCell(cell.label, cell.value));
3884
- if (named) return cleanValue(named.value);
3885
- const declarationName = declarationCoverageNameFromRow(row, children);
3886
- if (declarationName) return declarationName;
3887
- return coverageNameFromRow(row.textExcerpt ?? row.description ?? nodeText(row));
3888
- }
3889
- function legacyLimit(terms) {
3890
- return terms.find(
3891
- (term) => ["each_claim_limit", "each_occurrence_limit", "each_loss_limit", "aggregate_limit", "sublimit", "other"].includes(term.kind)
3892
- )?.value;
3893
- }
3894
- function legacyDeductible(terms) {
3895
- return terms.find((term) => term.kind === "deductible" || term.kind === "retention")?.value;
3896
- }
3897
- function legacyPremium(terms) {
3898
- return terms.find((term) => term.kind === "premium")?.value;
3899
- }
3900
- function retroDate(terms) {
3901
- return terms.find((term) => term.kind === "retroactive_date")?.value;
3902
- }
3903
- function hasCoverageLimitTerm(terms) {
3904
- return terms.some(
3905
- (term) => ["each_claim_limit", "each_occurrence_limit", "each_loss_limit", "aggregate_limit", "sublimit"].includes(term.kind) || term.kind === "other" && /\blimit\b/i.test(term.label) && moneyAmount(term.value) !== void 0
3906
- );
3907
- }
3908
- function isOperationalCoverageRow(coverage) {
3909
- const name = normalizeLabel(coverage.name);
3910
- if (!hasCoverageLimitTerm(coverage.limits)) return false;
3911
- if (/^(item\s+\d+|option:|annual policy premium|total premium|premium and payment)\b/i.test(coverage.name)) return false;
3912
- if (/^coverage part\s+s\b/i.test(coverage.name)) return false;
3913
- if (/^(nwc|iso|cg|il|acord)[-\s]?[a-z0-9]/i.test(coverage.name)) return false;
3914
- if (/\b(forms?|endorsements?|premium|payment|terrorism risk insurance act|tria|erp option|bilateral discovery)\b/i.test(name)) return false;
3915
- if (/\b(incurred in excess|shall erode|subject to|combined defense expenses)\b/i.test(name)) return false;
3916
- if (coverage.name.split(/\s+/).length > 16 && !/\b(coverage|liability|sub[-\s]?limit|aggregate|each\s+(claim|loss|occurrence)|limit)\b/i.test(coverage.name)) {
3917
- return false;
3918
- }
3919
- return /\b(coverage|coverage part|liability|sub[-\s]?limit|aggregate|each\s+(claim|loss|occurrence|proceeding)|bricking|cyber|privacy|media|regulatory|defense|fraud|social engineering|ai\/ml|errors?\s*&?\s*omissions|technology)\b/i.test(coverage.name);
3920
- }
3921
- function uniqueTerms(terms) {
3922
- const seen = /* @__PURE__ */ new Set();
3923
- const result = [];
3924
- for (const term of terms) {
3925
- const key = [term.kind, term.label.toLowerCase(), term.value.toLowerCase(), term.appliesTo ?? ""].join("|");
3926
- if (seen.has(key)) continue;
3927
- seen.add(key);
3928
- result.push(term);
3929
- }
3930
- return result;
3931
- }
3932
- function coverageFromTableRow(row, children, byId) {
3933
- if (isHeaderRow(row)) return void 0;
3934
- const name = nameFromRow(row, children);
3935
- const terms = uniqueTerms(termsFromRow(row, children));
3936
- if (!name || terms.length === 0) return void 0;
3937
- const ids = sourceIds([row, ...directChildren(row, children)]);
3938
- const endorsement = endorsementAncestor(row, byId);
3939
- return {
3940
- name,
3941
- limit: legacyLimit(terms),
3942
- deductible: legacyDeductible(terms),
3943
- premium: legacyPremium(terms),
3944
- retroactiveDate: retroDate(terms),
3945
- formNumber: typeof row.metadata?.formNumber === "string" ? row.metadata.formNumber : void 0,
3946
- sectionRef: endorsement?.title,
3947
- coverageOrigin: endorsement ? "endorsement" : "core",
3948
- endorsementNumber: endorsementNumberFrom(endorsement?.title),
3949
- limits: terms,
3950
- ...ids
3951
- };
3952
- }
3953
- function coverageFromEndorsement(endorsement, children) {
3954
- const rows = descendants(endorsement, children).filter((node) => node.kind === "table_row");
3955
- const terms = uniqueTerms(rows.flatMap(
3956
- (row) => termsFromRow(row, children).map((term) => {
3957
- const appliesTo = nameFromRow(row, children);
3958
- return appliesTo && term.label !== appliesTo ? { ...term, appliesTo } : term;
3959
- })
3960
- ));
3961
- if (terms.length === 0) return void 0;
3962
- const ids = sourceIds([endorsement, ...rows, ...rows.flatMap((row) => directChildren(row, children))]);
3963
- const name = endorsementNameFrom(endorsement);
3964
- return {
3965
- name,
3966
- limit: legacyLimit(terms),
3967
- deductible: legacyDeductible(terms),
3968
- premium: legacyPremium(terms),
3969
- retroactiveDate: retroDate(terms),
3970
- formNumber: typeof endorsement.metadata?.formNumber === "string" ? endorsement.metadata.formNumber : void 0,
3971
- sectionRef: name,
3972
- coverageOrigin: "endorsement",
3973
- endorsementNumber: endorsementNumberFrom(name),
3974
- limits: terms,
3975
- ...ids
3976
- };
3977
- }
3978
- function hasDescendantEndorsementWithTableRows(endorsement, children) {
3979
- return descendants(endorsement, children).some(
3980
- (node) => node.kind === "endorsement" && descendants(node, children).some((descendant) => descendant.kind === "table_row")
3981
- );
3982
- }
3983
- function buildCoverages(nodes) {
3984
- const children = childMap(nodes);
3985
- const byId = new Map(nodes.map((node) => [node.id, node]));
3986
- const coverages = [];
3987
- const seen = /* @__PURE__ */ new Set();
3988
- for (const endorsement of nodes.filter((node) => node.kind === "endorsement")) {
3989
- if (hasDescendantEndorsementWithTableRows(endorsement, children)) continue;
3990
- const coverage = coverageFromEndorsement(endorsement, children);
3991
- if (!coverage) continue;
3992
- const key = [coverage.name.toLowerCase(), coverage.sourceNodeIds.join(",")].join("|");
3993
- if (seen.has(key)) continue;
3994
- seen.add(key);
3995
- coverages.push(coverage);
3996
- }
3997
- const rows = nodes.filter((node) => {
3998
- const text = nodeText(node);
3999
- return (node.kind === "table_row" || node.kind === "schedule" || node.kind === "text") && /\b(coverage|limit|deductible|aggregate|occurrence|liability|premium|retroactive|retention)\b/i.test(text);
4000
- });
4001
- for (const row of rows) {
4002
- if (endorsementAncestor(row, byId)) continue;
4003
- const structured = row.kind === "table_row" ? coverageFromTableRow(row, children, byId) : void 0;
4004
- if (structured) {
4005
- if (!isOperationalCoverageRow(structured)) continue;
4006
- const key2 = [
4007
- structured.name.toLowerCase(),
4008
- structured.limits.map((term) => `${term.kind}:${term.value}`).join(","),
4009
- structured.sourceNodeIds.join(",")
4010
- ].join("|");
4011
- if (seen.has(key2)) continue;
4012
- seen.add(key2);
4013
- coverages.push(structured);
4014
- if (coverages.length >= 60) break;
4015
- continue;
4016
- }
4017
- const text = nodeText(row);
4018
- const name = coverageNameFromRow(text);
4019
- const limit = limitFromText(text);
4020
- const deductible = deductibleFromText(text);
4021
- const premium = premiumFromText(text);
4022
- if (!name || !limit && !deductible && !premium) continue;
4023
- if (!isOperationalCoverageRow({
4024
- name,
4025
- limit,
4026
- deductible,
4027
- premium,
4028
- coverageOrigin: "core",
4029
- limits: [
4030
- ...limit ? [{ kind: "other", label: "Limit", value: limit, sourceNodeIds: [row.id], sourceSpanIds: row.sourceSpanIds }] : [],
4031
- ...deductible ? [{ kind: "deductible", label: "Deductible", value: deductible, sourceNodeIds: [row.id], sourceSpanIds: row.sourceSpanIds }] : [],
4032
- ...premium ? [{ kind: "premium", label: "Premium", value: premium, sourceNodeIds: [row.id], sourceSpanIds: row.sourceSpanIds }] : []
4033
- ],
4034
- sourceNodeIds: [row.id],
4035
- sourceSpanIds: row.sourceSpanIds
4036
- })) continue;
4037
- const key = [name.toLowerCase(), limit, deductible, premium].join("|");
4038
- if (seen.has(key)) continue;
4039
- seen.add(key);
4040
- coverages.push({
4041
- name,
4042
- limit,
4043
- deductible,
4044
- premium,
4045
- formNumber: typeof row.metadata?.formNumber === "string" ? row.metadata.formNumber : void 0,
4046
- coverageOrigin: "core",
4047
- limits: [],
4048
- sourceNodeIds: [row.id],
4049
- sourceSpanIds: row.sourceSpanIds
4050
- });
4051
- if (coverages.length >= 60) break;
4052
- }
4053
- return coverages;
4054
- }
4055
- function buildParties(profile) {
4056
- const parties = [];
4057
- if (profile.namedInsured) {
4058
- parties.push({
4059
- role: "named_insured",
4060
- name: profile.namedInsured.normalizedValue ?? profile.namedInsured.value,
4061
- sourceNodeIds: profile.namedInsured.sourceNodeIds,
4062
- sourceSpanIds: profile.namedInsured.sourceSpanIds
4063
- });
4064
- }
4065
- if (profile.insurer) {
4066
- parties.push({
4067
- role: "insurer",
4068
- name: profile.insurer.normalizedValue ?? profile.insurer.value,
4069
- sourceNodeIds: profile.insurer.sourceNodeIds,
4070
- sourceSpanIds: profile.insurer.sourceSpanIds
4071
- });
4072
- }
4073
- if (profile.broker) {
4074
- parties.push({
4075
- role: "broker",
4076
- name: profile.broker.normalizedValue ?? profile.broker.value,
4077
- sourceNodeIds: profile.broker.sourceNodeIds,
4078
- sourceSpanIds: profile.broker.sourceSpanIds
4079
- });
4080
- }
4081
- return parties;
4082
- }
4083
- function buildEndorsementSupport(nodes) {
4084
- const support = [];
4085
- for (const node of nodes) {
4086
- const text = nodeText(node);
4087
- const add = (kind, status) => {
4088
- if (support.some((item) => item.kind === kind && item.status === status && item.sourceNodeIds.includes(node.id))) {
4089
- return;
4090
- }
4091
- support.push({
4092
- kind,
4093
- status,
4094
- summary: node.textExcerpt ?? node.description,
4095
- sourceNodeIds: [node.id],
4096
- sourceSpanIds: node.sourceSpanIds
4097
- });
4098
- };
4099
- if (/additional insured/i.test(text)) add("additional_insured", /not included|requires endorsement|only by endorsement/i.test(text) ? "requires_review" : "supported");
4100
- if (/waiver of subrogation|subrogation.*waived/i.test(text)) add("waiver_of_subrogation", /not included|requires endorsement|only by endorsement/i.test(text) ? "requires_review" : "supported");
4101
- if (/primary.*non[-\s]?contributory|non[-\s]?contributory/i.test(text)) add("primary_non_contributory", /not included|requires endorsement|only by endorsement/i.test(text) ? "requires_review" : "supported");
4102
- if (/loss payee|mortgagee/i.test(text)) add(/mortgagee/i.test(text) ? "mortgagee" : "loss_payee", "supported");
4103
- if (support.length >= 20) break;
4104
- }
4105
- return support;
4106
- }
4107
- function buildDeterministicOperationalProfile(params) {
4108
- const nodes = params.sourceTree.filter((node) => node.kind !== "document");
4109
- const partial = {
4110
- documentType: inferDocumentType(nodes),
4111
- policyTypes: inferPolicyTypes(nodes),
4112
- policyNumber: policyNumberFromNodes(nodes),
4113
- namedInsured: namedInsuredFromNodes(nodes),
4114
- insurer: insurerFromNodes(nodes),
4115
- broker: firstMatch(nodes, [
4116
- /\b(?:broker|producer|agent)\s*:?\s*([^|;\n]{3,120})/i
4117
- ]),
4118
- effectiveDate: firstMatch(nodes, [
4119
- /\b(?:effective date|policy period from|from)\s*:?\s*([0-9]{1,2}[/-][0-9]{1,2}[/-][0-9]{2,4})/i,
4120
- /\b([0-9]{1,2}[/-][0-9]{1,2}[/-][0-9]{2,4})\s+(?:to|through|-)\s+[0-9]{1,2}[/-][0-9]{1,2}[/-][0-9]{2,4}/i
4121
- ]),
4122
- expirationDate: firstMatch(nodes, [
4123
- /\b(?:expiration date|expiry date|expires|to)\s*:?\s*([0-9]{1,2}[/-][0-9]{1,2}[/-][0-9]{2,4})/i,
4124
- /\b[0-9]{1,2}[/-][0-9]{1,2}[/-][0-9]{2,4}\s+(?:to|through|-)\s+([0-9]{1,2}[/-][0-9]{1,2}[/-][0-9]{2,4})/i
4125
- ]),
4126
- retroactiveDate: firstMatch(nodes, [
4127
- /\bretroactive date\s*:?\s*([0-9]{1,2}[/-][0-9]{1,2}[/-][0-9]{2,4}|full prior acts|none)/i
4128
- ]),
4129
- premium: premiumFromNodes(nodes)
4130
- };
4131
- const coverages = buildCoverages(nodes);
4132
- const coverageTypes = [...new Set(coverages.map((coverage) => coverage.name))];
4133
- const sourceNodeIds = [.../* @__PURE__ */ new Set([
4134
- ...Object.values(partial).flatMap(
4135
- (value) => value && typeof value === "object" && "sourceNodeIds" in value ? value.sourceNodeIds : []
4136
- ),
4137
- ...coverages.flatMap((coverage) => coverage.sourceNodeIds)
4138
- ])];
4139
- const sourceSpanIds = [.../* @__PURE__ */ new Set([
4140
- ...Object.values(partial).flatMap(
4141
- (value) => value && typeof value === "object" && "sourceSpanIds" in value ? value.sourceSpanIds : []
4142
- ),
4143
- ...coverages.flatMap((coverage) => coverage.sourceSpanIds)
4144
- ])];
4145
- return PolicyOperationalProfileSchema.parse({
4146
- ...partial,
4147
- coverageTypes,
4148
- coverages,
4149
- parties: buildParties(partial),
4150
- endorsementSupport: buildEndorsementSupport(nodes),
4151
- sourceNodeIds,
4152
- sourceSpanIds,
4153
- warnings: [
4154
- ...coverages.length === 0 ? ["No source-backed coverage schedule rows were identified deterministically."] : [],
4155
- ...!partial.policyNumber ? ["No source-backed policy number was identified deterministically."] : [],
4156
- ...!partial.namedInsured ? ["No source-backed named insured was identified deterministically."] : []
4157
- ]
4158
- });
4159
- }
4160
- function mergeOperationalProfile(base, candidate, validNodeIds, validSpanIds) {
4161
- const keepIds = (ids, valid) => Array.isArray(ids) ? ids.filter((id) => typeof id === "string" && valid.has(id)) : [];
4162
- const mergeValue = (fallback, next) => {
4163
- if (!next || typeof next !== "object" || Array.isArray(next)) return fallback;
4164
- const record = next;
4165
- const value = typeof record.value === "string" ? cleanValue(record.value) : void 0;
4166
- if (!value) return fallback;
4167
- const sourceNodeIds = keepIds(record.sourceNodeIds, validNodeIds);
4168
- const sourceSpanIds = keepIds(record.sourceSpanIds, validSpanIds);
4169
- if (sourceNodeIds.length === 0 && sourceSpanIds.length === 0) return fallback;
4170
- return {
4171
- value,
4172
- normalizedValue: typeof record.normalizedValue === "string" ? record.normalizedValue : fallback?.normalizedValue,
4173
- confidence: record.confidence === "high" || record.confidence === "low" || record.confidence === "medium" ? record.confidence : "medium",
4174
- sourceNodeIds,
4175
- sourceSpanIds
4176
- };
4177
- };
4178
- const coverages = base.coverages.length > 0 ? base.coverages : Array.isArray(candidate.coverages) ? candidate.coverages.map((coverage) => {
4179
- const record = coverage;
4180
- const name = typeof record.name === "string" ? cleanValue(record.name) : void 0;
4181
- const sourceNodeIds = keepIds(record.sourceNodeIds, validNodeIds);
4182
- const sourceSpanIds = keepIds(record.sourceSpanIds, validSpanIds);
4183
- const limits = Array.isArray(record.limits) ? record.limits.filter(
4184
- (term) => Boolean(term) && typeof term === "object" && !Array.isArray(term)
4185
- ).flatMap((term) => {
4186
- const label = typeof term.label === "string" ? cleanValue(term.label) : void 0;
4187
- const value = typeof term.value === "string" ? cleanValue(term.value) : void 0;
4188
- const sourceNodeIds2 = keepIds(term.sourceNodeIds, validNodeIds);
4189
- const sourceSpanIds2 = keepIds(term.sourceSpanIds, validSpanIds);
4190
- if (!label || !value || sourceNodeIds2.length === 0 && sourceSpanIds2.length === 0) return [];
4191
- return [{
4192
- kind: normalizeTermKind(term.kind, label, value),
4193
- label,
4194
- value,
4195
- amount: typeof term.amount === "number" && Number.isFinite(term.amount) ? term.amount : void 0,
4196
- appliesTo: typeof term.appliesTo === "string" ? term.appliesTo : void 0,
4197
- sourceNodeIds: sourceNodeIds2,
4198
- sourceSpanIds: sourceSpanIds2
4199
- }];
4200
- }) : [];
4201
- return {
4202
- name,
4203
- coverageCode: typeof record.coverageCode === "string" ? cleanValue(record.coverageCode) : void 0,
4204
- limit: typeof record.limit === "string" ? cleanValue(record.limit) : void 0,
4205
- deductible: typeof record.deductible === "string" ? cleanValue(record.deductible) : void 0,
4206
- premium: typeof record.premium === "string" ? cleanValue(record.premium) : void 0,
4207
- retroactiveDate: typeof record.retroactiveDate === "string" ? cleanValue(record.retroactiveDate) : void 0,
4208
- formNumber: typeof record.formNumber === "string" ? cleanValue(record.formNumber) : void 0,
4209
- sectionRef: typeof record.sectionRef === "string" ? cleanValue(record.sectionRef) : void 0,
4210
- coverageOrigin: record.coverageOrigin === "core" || record.coverageOrigin === "endorsement" ? record.coverageOrigin : void 0,
4211
- endorsementNumber: typeof record.endorsementNumber === "string" ? cleanValue(record.endorsementNumber) : void 0,
4212
- limits,
4213
- sourceNodeIds,
4214
- sourceSpanIds
4215
- };
4216
- }).filter((coverage) => coverage.name && (coverage.sourceNodeIds.length > 0 || coverage.sourceSpanIds.length > 0)) : base.coverages;
4217
- return PolicyOperationalProfileSchema.parse({
4218
- ...base,
4219
- documentType: candidate.documentType === "quote" ? "quote" : candidate.documentType === "policy" ? "policy" : base.documentType,
4220
- policyTypes: Array.isArray(candidate.policyTypes) && candidate.policyTypes.length > 0 ? candidate.policyTypes : base.policyTypes,
4221
- policyNumber: mergeValue(base.policyNumber, candidate.policyNumber),
4222
- namedInsured: mergeValue(base.namedInsured, candidate.namedInsured),
4223
- insurer: mergeValue(base.insurer, candidate.insurer),
4224
- broker: mergeValue(base.broker, candidate.broker),
4225
- effectiveDate: mergeValue(base.effectiveDate, candidate.effectiveDate),
4226
- expirationDate: mergeValue(base.expirationDate, candidate.expirationDate),
4227
- retroactiveDate: mergeValue(base.retroactiveDate, candidate.retroactiveDate),
4228
- premium: mergeValue(base.premium, candidate.premium),
4229
- coverageTypes: Array.isArray(candidate.coverageTypes) && candidate.coverageTypes.length > 0 ? candidate.coverageTypes : base.coverageTypes,
4230
- coverages,
4231
- parties: base.parties,
4232
- endorsementSupport: base.endorsementSupport,
4233
- sourceNodeIds: [.../* @__PURE__ */ new Set([...base.sourceNodeIds, ...keepIds(candidate.sourceNodeIds, validNodeIds)])],
4234
- sourceSpanIds: [.../* @__PURE__ */ new Set([...base.sourceSpanIds, ...keepIds(candidate.sourceSpanIds, validSpanIds)])],
4235
- warnings: base.warnings
4236
- });
4237
- }
4238
-
4239
3365
  // src/extraction/pdf.ts
4240
3366
  var import_pdf_lib = require("pdf-lib");
4241
3367
  function isFileIdRef(input) {
@@ -10148,6 +9274,199 @@ function groundExtractionMemoryWithSourceSpans(memory, sourceSpans) {
10148
9274
  // src/extraction/source-tree-extractor.ts
10149
9275
  var import_zod42 = require("zod");
10150
9276
 
9277
+ // src/source/operational-profile.ts
9278
+ function normalizeWhitespace3(value) {
9279
+ return value.replace(/\s+/g, " ").trim();
9280
+ }
9281
+ function cleanValue(value) {
9282
+ if (!value) return void 0;
9283
+ return normalizeWhitespace3(value.replace(/^[\s:;#-]+|[\s;,.]+$/g, ""));
9284
+ }
9285
+ var OPERATIONAL_COVERAGE_TERM_KINDS = /* @__PURE__ */ new Set([
9286
+ "each_claim_limit",
9287
+ "each_occurrence_limit",
9288
+ "each_loss_limit",
9289
+ "aggregate_limit",
9290
+ "sublimit",
9291
+ "retention",
9292
+ "deductible",
9293
+ "retroactive_date",
9294
+ "premium",
9295
+ "other"
9296
+ ]);
9297
+ function normalizeTermKind(value) {
9298
+ return typeof value === "string" && OPERATIONAL_COVERAGE_TERM_KINDS.has(value) ? value : "other";
9299
+ }
9300
+ function mergeOperationalProfile(base, candidate, validNodeIds, validSpanIds) {
9301
+ const keepIds = (ids, valid) => Array.isArray(ids) ? ids.filter((id) => typeof id === "string" && valid.has(id)) : [];
9302
+ const mergeValue = (fallback, next) => {
9303
+ if (!next || typeof next !== "object" || Array.isArray(next)) return fallback;
9304
+ const record = next;
9305
+ const value = typeof record.value === "string" ? cleanValue(record.value) : void 0;
9306
+ if (!value) return fallback;
9307
+ const sourceNodeIds2 = keepIds(record.sourceNodeIds, validNodeIds);
9308
+ const sourceSpanIds2 = keepIds(record.sourceSpanIds, validSpanIds);
9309
+ if (sourceNodeIds2.length === 0 && sourceSpanIds2.length === 0) return fallback;
9310
+ return {
9311
+ value,
9312
+ normalizedValue: typeof record.normalizedValue === "string" ? record.normalizedValue : fallback?.normalizedValue,
9313
+ confidence: record.confidence === "high" || record.confidence === "low" || record.confidence === "medium" ? record.confidence : "medium",
9314
+ sourceNodeIds: sourceNodeIds2,
9315
+ sourceSpanIds: sourceSpanIds2
9316
+ };
9317
+ };
9318
+ const policyNumber = mergeValue(base.policyNumber, candidate.policyNumber);
9319
+ const namedInsured = mergeValue(base.namedInsured, candidate.namedInsured);
9320
+ const insurer = mergeValue(base.insurer, candidate.insurer);
9321
+ const broker = mergeValue(base.broker, candidate.broker);
9322
+ const effectiveDate = mergeValue(base.effectiveDate, candidate.effectiveDate);
9323
+ const expirationDate = mergeValue(base.expirationDate, candidate.expirationDate);
9324
+ const retroactiveDate = mergeValue(base.retroactiveDate, candidate.retroactiveDate);
9325
+ const premium = mergeValue(base.premium, candidate.premium);
9326
+ const sourceValues = [
9327
+ policyNumber,
9328
+ namedInsured,
9329
+ insurer,
9330
+ broker,
9331
+ effectiveDate,
9332
+ expirationDate,
9333
+ retroactiveDate,
9334
+ premium
9335
+ ].filter((value) => Boolean(value));
9336
+ const coverages = base.coverages.length > 0 ? base.coverages : Array.isArray(candidate.coverages) ? candidate.coverages.map((coverage) => {
9337
+ const record = coverage;
9338
+ const name = typeof record.name === "string" ? cleanValue(record.name) : void 0;
9339
+ const limits = Array.isArray(record.limits) ? record.limits.filter(
9340
+ (term) => Boolean(term) && typeof term === "object" && !Array.isArray(term)
9341
+ ).flatMap((term) => {
9342
+ const label = typeof term.label === "string" ? cleanValue(term.label) : void 0;
9343
+ const value = typeof term.value === "string" ? cleanValue(term.value) : void 0;
9344
+ const sourceNodeIds3 = keepIds(term.sourceNodeIds, validNodeIds);
9345
+ const sourceSpanIds3 = keepIds(term.sourceSpanIds, validSpanIds);
9346
+ if (!label || !value || sourceNodeIds3.length === 0 && sourceSpanIds3.length === 0) return [];
9347
+ return [{
9348
+ kind: normalizeTermKind(term.kind),
9349
+ label,
9350
+ value,
9351
+ amount: typeof term.amount === "number" && Number.isFinite(term.amount) ? term.amount : void 0,
9352
+ appliesTo: typeof term.appliesTo === "string" ? term.appliesTo : void 0,
9353
+ sourceNodeIds: sourceNodeIds3,
9354
+ sourceSpanIds: sourceSpanIds3
9355
+ }];
9356
+ }) : [];
9357
+ const sourceNodeIds2 = [.../* @__PURE__ */ new Set([
9358
+ ...keepIds(record.sourceNodeIds, validNodeIds),
9359
+ ...limits.flatMap((term) => term.sourceNodeIds)
9360
+ ])];
9361
+ const sourceSpanIds2 = [.../* @__PURE__ */ new Set([
9362
+ ...keepIds(record.sourceSpanIds, validSpanIds),
9363
+ ...limits.flatMap((term) => term.sourceSpanIds)
9364
+ ])];
9365
+ return {
9366
+ name,
9367
+ coverageCode: typeof record.coverageCode === "string" ? cleanValue(record.coverageCode) : void 0,
9368
+ limit: typeof record.limit === "string" ? cleanValue(record.limit) : void 0,
9369
+ deductible: typeof record.deductible === "string" ? cleanValue(record.deductible) : void 0,
9370
+ premium: typeof record.premium === "string" ? cleanValue(record.premium) : void 0,
9371
+ retroactiveDate: typeof record.retroactiveDate === "string" ? cleanValue(record.retroactiveDate) : void 0,
9372
+ formNumber: typeof record.formNumber === "string" ? cleanValue(record.formNumber) : void 0,
9373
+ sectionRef: typeof record.sectionRef === "string" ? cleanValue(record.sectionRef) : void 0,
9374
+ coverageOrigin: record.coverageOrigin === "core" || record.coverageOrigin === "endorsement" ? record.coverageOrigin : void 0,
9375
+ endorsementNumber: typeof record.endorsementNumber === "string" ? cleanValue(record.endorsementNumber) : void 0,
9376
+ limits,
9377
+ sourceNodeIds: sourceNodeIds2,
9378
+ sourceSpanIds: sourceSpanIds2
9379
+ };
9380
+ }).filter((coverage) => coverage.name && (coverage.sourceNodeIds.length > 0 || coverage.sourceSpanIds.length > 0)) : base.coverages;
9381
+ const sourceBackedParty = (role, value) => value ? {
9382
+ role,
9383
+ name: value.normalizedValue ?? value.value,
9384
+ sourceNodeIds: value.sourceNodeIds,
9385
+ sourceSpanIds: value.sourceSpanIds
9386
+ } : void 0;
9387
+ const candidateParties = Array.isArray(candidate.parties) ? candidate.parties.flatMap((party) => {
9388
+ if (!party || typeof party !== "object" || Array.isArray(party)) return [];
9389
+ const record = party;
9390
+ const role = typeof record.role === "string" ? cleanValue(record.role) : void 0;
9391
+ const name = typeof record.name === "string" ? cleanValue(record.name) : void 0;
9392
+ const sourceNodeIds2 = keepIds(record.sourceNodeIds, validNodeIds);
9393
+ const sourceSpanIds2 = keepIds(record.sourceSpanIds, validSpanIds);
9394
+ if (!role || !name || sourceNodeIds2.length === 0 && sourceSpanIds2.length === 0) return [];
9395
+ return [{ role, name, sourceNodeIds: sourceNodeIds2, sourceSpanIds: sourceSpanIds2 }];
9396
+ }) : [];
9397
+ const parties = [
9398
+ ...base.parties,
9399
+ ...candidateParties,
9400
+ sourceBackedParty("named_insured", namedInsured),
9401
+ sourceBackedParty("insurer", insurer),
9402
+ sourceBackedParty("broker", broker)
9403
+ ].filter((party) => Boolean(party)).filter(
9404
+ (party, index, rows) => rows.findIndex(
9405
+ (other) => other.role === party.role && other.name === party.name && other.sourceNodeIds.join(",") === party.sourceNodeIds.join(",") && other.sourceSpanIds.join(",") === party.sourceSpanIds.join(",")
9406
+ ) === index
9407
+ );
9408
+ const endorsementSupport = [
9409
+ ...base.endorsementSupport,
9410
+ ...Array.isArray(candidate.endorsementSupport) ? candidate.endorsementSupport.flatMap((row) => {
9411
+ if (!row || typeof row !== "object" || Array.isArray(row)) return [];
9412
+ const record = row;
9413
+ const kind = typeof record.kind === "string" ? cleanValue(record.kind) : void 0;
9414
+ const summary = typeof record.summary === "string" ? cleanValue(record.summary) : void 0;
9415
+ const status = record.status === "supported" || record.status === "excluded" || record.status === "requires_review" ? record.status : void 0;
9416
+ const sourceNodeIds2 = keepIds(record.sourceNodeIds, validNodeIds);
9417
+ const sourceSpanIds2 = keepIds(record.sourceSpanIds, validSpanIds);
9418
+ if (!kind || !summary || !status || sourceNodeIds2.length === 0 && sourceSpanIds2.length === 0) return [];
9419
+ return [{ kind, status, summary, sourceNodeIds: sourceNodeIds2, sourceSpanIds: sourceSpanIds2 }];
9420
+ }) : []
9421
+ ].filter(
9422
+ (row, index, rows) => rows.findIndex(
9423
+ (other) => other.kind === row.kind && other.status === row.status && other.summary === row.summary && other.sourceNodeIds.join(",") === row.sourceNodeIds.join(",") && other.sourceSpanIds.join(",") === row.sourceSpanIds.join(",")
9424
+ ) === index
9425
+ );
9426
+ const sourceNodeIds = [.../* @__PURE__ */ new Set([
9427
+ ...base.sourceNodeIds,
9428
+ ...keepIds(candidate.sourceNodeIds, validNodeIds),
9429
+ ...sourceValues.flatMap((value) => value.sourceNodeIds),
9430
+ ...coverages.flatMap((coverage) => coverage.sourceNodeIds),
9431
+ ...coverages.flatMap((coverage) => coverage.limits.flatMap((term) => term.sourceNodeIds)),
9432
+ ...parties.flatMap((party) => party.sourceNodeIds),
9433
+ ...endorsementSupport.flatMap((row) => row.sourceNodeIds)
9434
+ ])];
9435
+ const sourceSpanIds = [.../* @__PURE__ */ new Set([
9436
+ ...base.sourceSpanIds,
9437
+ ...keepIds(candidate.sourceSpanIds, validSpanIds),
9438
+ ...sourceValues.flatMap((value) => value.sourceSpanIds),
9439
+ ...coverages.flatMap((coverage) => coverage.sourceSpanIds),
9440
+ ...coverages.flatMap((coverage) => coverage.limits.flatMap((term) => term.sourceSpanIds)),
9441
+ ...parties.flatMap((party) => party.sourceSpanIds),
9442
+ ...endorsementSupport.flatMap((row) => row.sourceSpanIds)
9443
+ ])];
9444
+ const warnings = [
9445
+ ...base.warnings,
9446
+ ...Array.isArray(candidate.warnings) ? candidate.warnings.filter((warning) => typeof warning === "string" && warning.trim().length > 0) : []
9447
+ ];
9448
+ return PolicyOperationalProfileSchema.parse({
9449
+ ...base,
9450
+ documentType: candidate.documentType === "quote" ? "quote" : candidate.documentType === "policy" ? "policy" : base.documentType,
9451
+ policyTypes: Array.isArray(candidate.policyTypes) && candidate.policyTypes.length > 0 ? candidate.policyTypes : base.policyTypes,
9452
+ policyNumber,
9453
+ namedInsured,
9454
+ insurer,
9455
+ broker,
9456
+ effectiveDate,
9457
+ expirationDate,
9458
+ retroactiveDate,
9459
+ premium,
9460
+ coverageTypes: Array.isArray(candidate.coverageTypes) && candidate.coverageTypes.length > 0 ? candidate.coverageTypes : base.coverageTypes,
9461
+ coverages,
9462
+ parties,
9463
+ endorsementSupport,
9464
+ sourceNodeIds,
9465
+ sourceSpanIds,
9466
+ warnings: [...new Set(warnings)]
9467
+ });
9468
+ }
9469
+
10151
9470
  // src/extraction/operational-profile-cleanup.ts
10152
9471
  var import_zod41 = require("zod");
10153
9472
  var OPERATIONAL_COVERAGE_TERM_KINDS2 = [
@@ -11384,8 +10703,8 @@ function applyTitleSectionHierarchy(sourceTree) {
11384
10703
  const current = updates.get(child.id) ?? child;
11385
10704
  const heading = sectionHeadingTitle(current, container);
11386
10705
  if (heading) {
11387
- const descendants2 = byParent.get(child.id) ?? [];
11388
- const hasOwnContent = descendants2.some((descendant) => descendant.kind !== "table_row" && descendant.kind !== "table_cell");
10706
+ const descendants = byParent.get(child.id) ?? [];
10707
+ const hasOwnContent = descendants.some((descendant) => descendant.kind !== "table_row" && descendant.kind !== "table_cell");
11389
10708
  let hasFollowingContent = false;
11390
10709
  for (const nextChild of directPageChildren.slice(index + 1)) {
11391
10710
  const next = updates.get(nextChild.id) ?? nextChild;
@@ -11403,7 +10722,7 @@ function applyTitleSectionHierarchy(sourceTree) {
11403
10722
  parentId: container.id,
11404
10723
  kind: sectionKindForTitle(heading),
11405
10724
  title: heading,
11406
- description: descriptionWithPages(cleanText([heading, "section"].join(" "), heading), [current, ...descendants2]),
10725
+ description: descriptionWithPages(cleanText([heading, "section"].join(" "), heading), [current, ...descendants]),
11407
10726
  metadata: {
11408
10727
  ...current.metadata,
11409
10728
  organizer: "title_section",
@@ -11727,8 +11046,32 @@ ${JSON.stringify(nodes, null, 2)}
11727
11046
 
11728
11047
  Return JSON with labels and groups only.`;
11729
11048
  }
11730
- function buildOperationalProfilePrompt(sourceTree, fallback) {
11731
- const nodes = sourceTree.filter((node) => node.kind !== "document").slice(0, 240).map(compactNode2);
11049
+ function operationalProfilePromptNodes(sourceTree) {
11050
+ return sourceTree.filter((node) => node.kind !== "document").filter((node) => {
11051
+ if (["page_group", "form", "endorsement", "schedule", "table", "table_row", "table_cell"].includes(node.kind)) {
11052
+ return true;
11053
+ }
11054
+ const text = [node.title, node.path, node.description, node.textExcerpt].filter(Boolean).join(" ");
11055
+ 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);
11056
+ }).slice(0, 420);
11057
+ }
11058
+ function emptyOperationalProfile() {
11059
+ return {
11060
+ documentType: "policy",
11061
+ policyTypes: ["other"],
11062
+ coverageTypes: [],
11063
+ coverages: [],
11064
+ parties: [],
11065
+ endorsementSupport: [],
11066
+ sourceNodeIds: [],
11067
+ sourceSpanIds: [],
11068
+ warnings: []
11069
+ };
11070
+ }
11071
+ function buildOperationalProfilePrompt(sourceTree) {
11072
+ const nodes = operationalProfilePromptNodes(sourceTree).map(
11073
+ (node) => compactNode2(node, node.kind === "page" || node.kind === "endorsement" ? 900 : 700)
11074
+ );
11732
11075
  return `Extract a source-backed operational profile for an insurance policy or quote.
11733
11076
 
11734
11077
  Return only high-value operational facts needed for policy lists, Q&A, compliance, and certificate generation:
@@ -11745,9 +11088,7 @@ Rules:
11745
11088
  - For coverage schedules, put each claim, aggregate, sublimit, retention, deductible, and retroactive date values in coverages[].limits with labels and source IDs. Keep the legacy coverages[].limit as the primary display value only.
11746
11089
  - Use coverageOrigin: "endorsement" for endorsement units and "core" for declarations/core policy coverage units.
11747
11090
  - Do not copy entire policy wording into fields.
11748
-
11749
- Deterministic baseline:
11750
- ${JSON.stringify(fallback, null, 2)}
11091
+ - Extract facts directly from source nodes. There is no deterministic fact baseline.
11751
11092
 
11752
11093
  Source nodes:
11753
11094
  ${JSON.stringify(nodes, null, 2)}
@@ -11757,7 +11098,6 @@ Return JSON for the operational profile.`;
11757
11098
  var VISUAL_TABLE_REPAIR_MAX_TABLES = 4;
11758
11099
  var VISUAL_TABLE_REPAIR_MAX_ROWS = 28;
11759
11100
  var VISUAL_TABLE_REPAIR_MAX_CELLS = 140;
11760
- var VISUAL_TABLE_REPAIR_KEYWORDS = /\b(coverage|limit|limits?|deductible|retroactive|premium|tax|fee|sublimit|sub-limit|aggregate|claim|occurrence|retention|declarations?)\b/i;
11761
11101
  function isSourceTreeHeaderRow(row) {
11762
11102
  return row.metadata?.isHeader === true || row.metadata?.isHeader === "true";
11763
11103
  }
@@ -11803,13 +11143,11 @@ function primaryHeaderColumnCount(rows) {
11803
11143
  if (header) return header.cells.length;
11804
11144
  return Math.max(0, ...rows.map(({ cells }) => cells.length));
11805
11145
  }
11806
- function shouldRepairVisualTable(candidate) {
11146
+ function visualTableRepairScore(candidate) {
11807
11147
  const rows = candidate.rows;
11808
- if (rows.length < 3) return false;
11809
- const tableText = rows.map(({ row, cells }) => tableRowTextForPrompt(row, cells)).join(" ");
11810
- if (!VISUAL_TABLE_REPAIR_KEYWORDS.test(tableText)) return false;
11148
+ if (rows.length < 3) return 0;
11811
11149
  const headerCount = primaryHeaderColumnCount(rows);
11812
- if (headerCount < 2) return false;
11150
+ if (headerCount < 2) return 0;
11813
11151
  const firstHeaderIndex = rows.findIndex(({ row, cells }) => isSourceTreeHeaderRow(row) && cells.length > 1);
11814
11152
  const repeatedHeader = firstHeaderIndex >= 0 && rows.some(({ row }, index) => index > firstHeaderIndex && isSourceTreeHeaderRow(row));
11815
11153
  const shortContinuation = rows.some(
@@ -11821,7 +11159,7 @@ function shouldRepairVisualTable(candidate) {
11821
11159
  const danglingSlash = rows.some(
11822
11160
  ({ row, cells }) => !isSourceTreeHeaderRow(row) && /\/\s*$/.test(tableRowTextForPrompt(row, cells))
11823
11161
  );
11824
- return repeatedHeader || shortContinuation || genericDataLabels || danglingSlash;
11162
+ return (repeatedHeader ? 4 : 0) + (danglingSlash ? 3 : 0) + (shortContinuation ? 3 : 0) + (genericDataLabels ? 2 : 0);
11825
11163
  }
11826
11164
  function visualTableCandidates(sourceTree) {
11827
11165
  const byParent = nodesByParent(sourceTree);
@@ -11829,9 +11167,9 @@ function visualTableCandidates(sourceTree) {
11829
11167
  table,
11830
11168
  page: table.pageStart,
11831
11169
  rows: tableRowsWithCells(table, byParent)
11832
- })).filter(shouldRepairVisualTable).sort(
11833
- (left, right) => left.page - right.page || left.table.order - right.table.order || left.table.id.localeCompare(right.table.id)
11834
- ).slice(0, VISUAL_TABLE_REPAIR_MAX_TABLES);
11170
+ })).map((candidate) => ({ ...candidate, repairScore: visualTableRepairScore(candidate) })).filter((candidate) => candidate.repairScore > 0).sort(
11171
+ (left, right) => right.repairScore - left.repairScore || left.page - right.page || left.table.order - right.table.order || left.table.id.localeCompare(right.table.id)
11172
+ ).slice(0, VISUAL_TABLE_REPAIR_MAX_TABLES).map(({ repairScore: _repairScore, ...candidate }) => candidate);
11835
11173
  }
11836
11174
  function compactVisualTableCandidate(candidate) {
11837
11175
  let cellCount = 0;
@@ -12389,11 +11727,8 @@ async function runSourceTreeExtraction(params) {
12389
11727
  });
12390
11728
  sourceTree = visualTableRepair.sourceTree;
12391
11729
  warnings.push(...visualTableRepair.warnings);
12392
- const deterministicProfile = buildDeterministicOperationalProfile({
12393
- sourceTree,
12394
- sourceSpans
12395
- });
12396
- let operationalProfile = deterministicProfile;
11730
+ const emptyProfile = emptyOperationalProfile();
11731
+ let operationalProfile = emptyProfile;
12397
11732
  try {
12398
11733
  const validNodeIds = new Set(sourceTree.map((node) => node.id));
12399
11734
  const validSpanIds = new Set(sourceSpans.map((span) => span.id));
@@ -12402,7 +11737,7 @@ async function runSourceTreeExtraction(params) {
12402
11737
  const response = await safeGenerateObject(
12403
11738
  params.generateObject,
12404
11739
  {
12405
- prompt: buildOperationalProfilePrompt(sourceTree, deterministicProfile),
11740
+ prompt: buildOperationalProfilePrompt(sourceTree),
12406
11741
  schema: OperationalProfilePromptSchema,
12407
11742
  maxTokens: budget.maxTokens,
12408
11743
  taskKind: "extraction_operational_profile",
@@ -12410,7 +11745,7 @@ async function runSourceTreeExtraction(params) {
12410
11745
  providerOptions: params.providerOptions
12411
11746
  },
12412
11747
  {
12413
- fallback: deterministicProfile,
11748
+ fallback: emptyProfile,
12414
11749
  log: params.log
12415
11750
  }
12416
11751
  );
@@ -12421,19 +11756,19 @@ async function runSourceTreeExtraction(params) {
12421
11756
  durationMs: Date.now() - startedAt
12422
11757
  });
12423
11758
  operationalProfile = mergeOperationalProfile(
12424
- deterministicProfile,
11759
+ emptyProfile,
12425
11760
  response.object,
12426
11761
  validNodeIds,
12427
11762
  validSpanIds
12428
11763
  );
12429
11764
  } catch (error) {
12430
- warnings.push(`Operational profile model pass failed; deterministic profile used (${error instanceof Error ? error.message : String(error)})`);
11765
+ warnings.push(`Operational profile model pass failed; coverage rows omitted (${error instanceof Error ? error.message : String(error)})`);
12431
11766
  }
12432
11767
  if (operationalProfile.coverages.length > 0) {
12433
11768
  try {
12434
11769
  const validNodeIds = new Set(sourceTree.map((node) => node.id));
12435
11770
  const validSpanIds = new Set(sourceSpans.map((span) => span.id));
12436
- const budget = params.resolveBudget("extraction_operational_profile", 4096);
11771
+ const budget = params.resolveBudget("extraction_coverage_cleanup", 4096);
12437
11772
  const startedAt = Date.now();
12438
11773
  const response = await safeGenerateObject(
12439
11774
  params.generateObject,
@@ -12441,9 +11776,10 @@ async function runSourceTreeExtraction(params) {
12441
11776
  prompt: buildOperationalProfileCleanupPrompt(sourceTree, operationalProfile),
12442
11777
  schema: OperationalProfileCleanupSchema,
12443
11778
  maxTokens: budget.maxTokens,
12444
- taskKind: "extraction_operational_profile",
11779
+ taskKind: "extraction_coverage_cleanup",
12445
11780
  budgetDiagnostics: budget,
12446
- providerOptions: params.providerOptions
11781
+ providerOptions: params.providerOptions,
11782
+ trace: { phase: "coverage_cleanup", sourceBacked: true }
12447
11783
  },
12448
11784
  {
12449
11785
  fallback: { coverageDecisions: [], warnings: [] },
@@ -12451,7 +11787,7 @@ async function runSourceTreeExtraction(params) {
12451
11787
  }
12452
11788
  );
12453
11789
  localTrack(response.usage, {
12454
- taskKind: "extraction_operational_profile",
11790
+ taskKind: "extraction_coverage_cleanup",
12455
11791
  label: "operational_profile_cleanup",
12456
11792
  maxTokens: budget.maxTokens,
12457
11793
  durationMs: Date.now() - startedAt
@@ -17980,7 +17316,6 @@ var AGENT_TOOLS = [
17980
17316
  buildConfirmationSummaryPrompt,
17981
17317
  buildConversationMemoryGuidance,
17982
17318
  buildCoverageGapPrompt,
17983
- buildDeterministicOperationalProfile,
17984
17319
  buildDoclingProviderOptions,
17985
17320
  buildDocumentSourceTree,
17986
17321
  buildFieldExplanationPrompt,
@@ -18037,7 +17372,6 @@ var AGENT_TOOLS = [
18037
17372
  getTemplate,
18038
17373
  isDoclingExtractionInput,
18039
17374
  isFileReference,
18040
- mergeOperationalProfile,
18041
17375
  mergeQuestionAnswers,
18042
17376
  mergeSourceSpans,
18043
17377
  normalizeApplicationQuestionGraph,