@claritylabs/cl-sdk 3.2.8 → 3.2.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -3576,16 +3576,6 @@ var OPERATIONAL_COVERAGE_TERM_KINDS2 = [
3576
3576
  "other"
3577
3577
  ];
3578
3578
  var OperationalCoverageTermKindSchema = z21.enum(OPERATIONAL_COVERAGE_TERM_KINDS2);
3579
- var CleanupTermSchema = z21.object({
3580
- kind: OperationalCoverageTermKindSchema,
3581
- label: z21.string(),
3582
- value: z21.string(),
3583
- amount: z21.number().nullable().optional(),
3584
- appliesTo: z21.string().nullable().optional(),
3585
- sourceNodeIds: z21.array(z21.string()).optional(),
3586
- sourceSpanIds: z21.array(z21.string()).optional(),
3587
- reason: z21.string().optional()
3588
- });
3589
3579
  var OperationalProfileCleanupSchema = z21.object({
3590
3580
  coverageDecisions: z21.array(z21.object({
3591
3581
  coverageIndex: z21.number().int().nonnegative(),
@@ -3609,8 +3599,7 @@ var OperationalProfileCleanupSchema = z21.object({
3609
3599
  appliesTo: z21.string().nullable().optional(),
3610
3600
  sourceNodeIds: z21.array(z21.string()).optional(),
3611
3601
  sourceSpanIds: z21.array(z21.string()).optional()
3612
- })).optional(),
3613
- termAdditions: z21.array(CleanupTermSchema).optional()
3602
+ })).optional()
3614
3603
  })).default([]),
3615
3604
  warnings: z21.array(z21.string()).default([])
3616
3605
  });
@@ -3618,7 +3607,6 @@ var CLEANUP_CANDIDATE_ID_LIMIT = 12;
3618
3607
  var CLEANUP_SOURCE_NODE_LIMIT = 90;
3619
3608
  var CLEANUP_SIBLING_WINDOW = 4;
3620
3609
  var CLEANUP_KEYWORD = /\b(coverage|limit|liability|deductible|retention|retroactive|premium|aggregate|sublimit|sub-limit|claim|occurrence|loss|proceeding|endorsement|declarations?)\b|\$[0-9]/i;
3621
- var CLEANUP_CONTINUATION_KEYWORD = /\b(coverage part|limit of liability|deductible|retention|retroactive date|aggregate|sublimit|sub-limit|each claim|each occurrence|each loss|each proceeding|coinsurance)\b|\$[0-9]/i;
3622
3610
  function compactNode(node, maxText = 700) {
3623
3611
  return {
3624
3612
  id: node.id,
@@ -3747,15 +3735,11 @@ function selectCoverageCleanupNodes(sourceTree, coverages) {
3747
3735
  const children = childrenByParent.get(selectedNode.id) ?? [];
3748
3736
  for (const child of children.slice(0, 24)) addNode(child, 760);
3749
3737
  }
3750
- const continuationPages = new Set([...coveragePages].map((page) => page + 1));
3751
3738
  for (const node of sourceTree) {
3752
3739
  if (node.kind === "document") continue;
3753
- if (typeof node.pageStart !== "number") continue;
3754
- const samePage = coveragePages.has(node.pageStart);
3755
- const nearbyContinuationPage = continuationPages.has(node.pageStart);
3756
- if (!samePage && !nearbyContinuationPage) continue;
3740
+ if (!node.pageStart || !coveragePages.has(node.pageStart)) continue;
3757
3741
  const text = nodeTextForSelection(node);
3758
- if (CLEANUP_KEYWORD.test(text) || nodeTextMatchesCoverage(node, coverageTerms) || nearbyContinuationPage && CLEANUP_CONTINUATION_KEYWORD.test(text)) {
3742
+ if (CLEANUP_KEYWORD.test(text) || nodeTextMatchesCoverage(node, coverageTerms)) {
3759
3743
  addNode(node, 600);
3760
3744
  }
3761
3745
  }
@@ -3769,7 +3753,7 @@ function buildOperationalProfileCleanupPrompt(sourceTree, profile, options = {})
3769
3753
  const coverageEntries = coverageCleanupEntries(profile, options.coverageIndexes);
3770
3754
  const nodes = selectCoverageCleanupNodes(sourceTree, coverageEntries.map((entry) => entry.coverage)).map((node) => compactNode(
3771
3755
  node,
3772
- node.kind === "page" || node.kind === "page_group" ? 520 : node.kind === "table_row" || node.kind === "table_cell" ? 520 : 360
3756
+ node.kind === "page" || node.kind === "page_group" ? 260 : node.kind === "table_row" || node.kind === "table_cell" ? 520 : 360
3773
3757
  ));
3774
3758
  const candidate = {
3775
3759
  documentType: profile.documentType,
@@ -3793,23 +3777,20 @@ Projection defects to look for:
3793
3777
  - Item references such as "shown in Item 7" or bare item numbers treated as money amounts.
3794
3778
  - Policy wording, exclusions, or unsupported prose copied into operational limit/deductible fields.
3795
3779
  - Header/value splits where "Limit of Liability", "Deductible", "Retroactive Date", "Aggregate", "Each Claim", or similar terms are attached to the wrong coverage row.
3796
- - Collapsed combined bases where one candidate term says "Each Claim / Aggregate", "Each Loss / Aggregate", "Each Proceeding / Aggregate", or a continuation line supplies a separate policy aggregate, sub-limit, deductible, retention, retroactive date, or coinsurance term.
3797
3780
  - Repeated schedule headings projected as separate coverages when they only introduce the next coverage group.
3798
3781
 
3799
3782
  Rules:
3800
3783
  - Use internal reasoning, but return JSON decisions only.
3801
- - Do not invent policy facts. Keep, drop, or update only existing coverageIndex and termIndex entries, except for source-backed termAdditions on an existing coverage.
3784
+ - Do not invent policy facts. Keep, drop, or update only existing coverageIndex and termIndex entries.
3802
3785
  - Use sourceNodeIds and sourceSpanIds only from the provided source nodes or from the existing candidate entry.
3803
3786
  - Prefer dropping a malformed fact over speculative rewriting.
3804
3787
  - Keep a coverage when it is a real operational coverage/benefit even if only one term needs cleanup.
3805
3788
  - Never drop a declaration or schedule coverage row that names a coverage and states policy-specific amounts, dates, deductibles, retentions, premiums, or coverage terms. Repair its terms instead.
3806
3789
  - When changing a term's semantic meaning, set kind to the corrected normalized term kind.
3807
- - Do not add new coverage rows.
3808
- - Add termAdditions only when a provided source node directly supports the missing term and the candidate already has the correct coverage row.
3809
- - When replacing one combined term with split terms, drop the combined term and add one term per basis. For example, "$1,000,000 Each Claim / Aggregate" should become one each_claim_limit term and one aggregate_limit term, both with value "$1,000,000".
3810
- - When a schedule continues onto the next page before the next item marker, attach continuation terms such as "Aggregate", "Policy Aggregate", coinsurance, deductible, or retroactive date to the previous coverage row.
3790
+ - Do not add new coverage rows or new terms; this pass cleans the existing projection.
3791
+ - If one existing term combines multiple real limit bases, such as "Each Claim / Aggregate", keep the combined term unless another existing term already represents the other basis. Do not relabel it to only one basis and lose information.
3811
3792
  - Include every JSON key in each decision. Use null for scalar fields you are not changing and [] for source ID lists you are not changing.
3812
- - For each coverage decision, always include termDecisions and termAdditions. Use [] when no existing terms need cleanup or no new terms are needed.
3793
+ - For each coverage decision, always include termDecisions. Use [] when no terms need cleanup.
3813
3794
  - Keep reasons concise and factual.
3814
3795
 
3815
3796
  Candidate projection:
@@ -3950,50 +3931,9 @@ function applyTermCleanupDecision(term, decision, validNodeIds, validSpanIds) {
3950
3931
  }
3951
3932
  return next;
3952
3933
  }
3953
- function termAdditionTouches(addition, predicate) {
3954
- return predicate({
3955
- kind: addition.kind,
3956
- label: cleanProfileValue(addition.label) ?? "",
3957
- value: cleanProfileValue(addition.value) ?? ""
3958
- });
3959
- }
3960
3934
  function termDecisionsTouch(coverage, decisions, predicate) {
3961
3935
  return decisions.filter((decision) => decision.action !== "keep").some((decision) => termDecisionTouches(coverage, decision, predicate));
3962
3936
  }
3963
- function termAdditionsTouch(additions, predicate) {
3964
- return additions.some((addition) => termAdditionTouches(addition, predicate));
3965
- }
3966
- function termKey(term) {
3967
- return [
3968
- term.kind,
3969
- cleanProfileValue(term.label)?.toLowerCase(),
3970
- cleanProfileValue(term.value)?.toLowerCase()
3971
- ].join("|");
3972
- }
3973
- function applyTermAddition(addition, validNodeIds, validSpanIds) {
3974
- const label = cleanProfileValue(addition.label);
3975
- const value = cleanProfileValue(addition.value);
3976
- if (!label || !value) return void 0;
3977
- const sourceNodeIds = validIds(addition.sourceNodeIds, validNodeIds);
3978
- const sourceSpanIds = validIds(addition.sourceSpanIds, validSpanIds);
3979
- if (sourceNodeIds.length === 0 && sourceSpanIds.length === 0) return void 0;
3980
- const term = {
3981
- kind: addition.kind,
3982
- label,
3983
- value,
3984
- sourceNodeIds,
3985
- sourceSpanIds
3986
- };
3987
- if (typeof addition.amount === "number" && Number.isFinite(addition.amount)) {
3988
- term.amount = addition.amount;
3989
- } else if (term.kind !== "retroactive_date") {
3990
- const amount = amountFromOperationalValue(value);
3991
- if (amount !== void 0) term.amount = amount;
3992
- }
3993
- const appliesTo = cleanProfileValue(addition.appliesTo);
3994
- if (appliesTo) term.appliesTo = appliesTo;
3995
- return term;
3996
- }
3997
3937
  function applyCoverageCleanupDecision(coverage, decision, validNodeIds, validSpanIds) {
3998
3938
  if (!decision || decision.action === "keep") return coverage;
3999
3939
  if (decision.action === "drop") return void 0;
@@ -4024,31 +3964,23 @@ function applyCoverageCleanupDecision(coverage, decision, validNodeIds, validSpa
4024
3964
  const termDecisions = (decision.termDecisions ?? []).filter((termDecision) => termDecision.termIndex < coverage.limits.length);
4025
3965
  const termDecisionByIndex = new Map(termDecisions.map((termDecision) => [termDecision.termIndex, termDecision]));
4026
3966
  next.limits = coverage.limits.map((term, index) => applyTermCleanupDecision(term, termDecisionByIndex.get(index), validNodeIds, validSpanIds)).filter((term) => Boolean(term));
4027
- const termAdditions = (decision.termAdditions ?? []).map((addition) => applyTermAddition(addition, validNodeIds, validSpanIds)).filter((term) => Boolean(term));
4028
- const existingTermKeys = new Set(next.limits.map(termKey));
4029
- for (const term of termAdditions) {
4030
- const key = termKey(term);
4031
- if (existingTermKeys.has(key)) continue;
4032
- next.limits.push(term);
4033
- existingTermKeys.add(key);
4034
- }
4035
- if (termDecisions.length > 0 || termAdditions.length > 0) {
4036
- if (decision.limit == null && (termDecisionsTouch(coverage, termDecisions, isLimitTerm) || termAdditionsTouch(termAdditions, isLimitTerm))) {
3967
+ if (termDecisions.length > 0) {
3968
+ if (decision.limit == null && termDecisionsTouch(coverage, termDecisions, isLimitTerm)) {
4037
3969
  const value = primaryLimitFromTerms(next.limits);
4038
3970
  if (value) next.limit = value;
4039
3971
  else delete next.limit;
4040
3972
  }
4041
- if (decision.deductible == null && (termDecisionsTouch(coverage, termDecisions, isDeductibleTerm) || termAdditionsTouch(termAdditions, isDeductibleTerm))) {
3973
+ if (decision.deductible == null && termDecisionsTouch(coverage, termDecisions, isDeductibleTerm)) {
4042
3974
  const value = deductibleFromTerms(next.limits);
4043
3975
  if (value) next.deductible = value;
4044
3976
  else delete next.deductible;
4045
3977
  }
4046
- if (decision.premium == null && (termDecisionsTouch(coverage, termDecisions, isPremiumTerm) || termAdditionsTouch(termAdditions, isPremiumTerm))) {
3978
+ if (decision.premium == null && termDecisionsTouch(coverage, termDecisions, isPremiumTerm)) {
4047
3979
  const value = premiumFromTerms(next.limits);
4048
3980
  if (value) next.premium = value;
4049
3981
  else delete next.premium;
4050
3982
  }
4051
- if (decision.retroactiveDate == null && (termDecisionsTouch(coverage, termDecisions, isRetroactiveDateTerm) || termAdditionsTouch(termAdditions, isRetroactiveDateTerm))) {
3983
+ if (decision.retroactiveDate == null && termDecisionsTouch(coverage, termDecisions, isRetroactiveDateTerm)) {
4052
3984
  const value = retroactiveDateFromTerms(next.limits);
4053
3985
  if (value) next.retroactiveDate = value;
4054
3986
  else delete next.retroactiveDate;
@@ -4275,36 +4207,6 @@ function spanPageEnd(span) {
4275
4207
  function spanSourceUnit(span) {
4276
4208
  return span.sourceUnit ?? span.metadata?.sourceUnit ?? span.metadata?.elementType;
4277
4209
  }
4278
- function formNumberFromText(value) {
4279
- return cleanText(value, "").match(/\b[A-Z]{2,}(?:-[A-Z0-9]+)+\s+\d{2}\s+\d{2}\b/)?.[0]?.replace(/\s+/g, " ");
4280
- }
4281
- function editionDateFromFormNumber(formNumber) {
4282
- const match = formNumber?.match(/\b(\d{2})\s+(\d{2})$/);
4283
- return match ? `${match[1]}/${match[2]}` : void 0;
4284
- }
4285
- function pageTitleFromText(text, fallback) {
4286
- const normalized = cleanText(text, fallback);
4287
- const patterns = [
4288
- /\bIMPORTANT NOTICE\s+[—-]\s+HOW TO REPORT A CLAIM\b/i,
4289
- /\bPRIVACY NOTICE TO POLICYHOLDERS\b/i,
4290
- /\bOFAC ADVISORY NOTICE\b/i,
4291
- /\bTERRORISM RISK INSURANCE ACT\s*\(TRIA\)\s*DISCLOSURE AND REJECTION\b/i,
4292
- /\bDECLARATIONS PAGE\b/i,
4293
- /\bTECHNOLOGY ERRORS?\s*&\s*OMISSIONS AND CYBER LIABILITY INSURANCE POLICY\b/i,
4294
- /\bTRADE OR ECONOMIC SANCTIONS LIMITATION\b/i,
4295
- /\bFORMS? AND ENDORSEMENTS\b/i
4296
- ];
4297
- for (const pattern of patterns) {
4298
- const match = normalized.match(pattern)?.[0];
4299
- if (match) return cleanText(match, fallback);
4300
- }
4301
- const endorsement = normalized.match(/\bENDORSEMENT\s+(?:NO\.?|NUMBER|#)\s*[A-Z0-9][A-Z0-9.-]*\b/i)?.[0];
4302
- if (endorsement) return cleanText(endorsement, fallback);
4303
- const firstSentence = normalized.split(/(?<=\.)\s+/)[0];
4304
- if (/^page\s+\d+\b/i.test(firstSentence)) return fallback;
4305
- if (firstSentence && firstSentence.length <= 120) return firstSentence.replace(/[.]$/, "");
4306
- return fallback;
4307
- }
4308
4210
  function pageHeadingTitleFromText(text, fallback) {
4309
4211
  const normalized = cleanText(text, "");
4310
4212
  const headingText = normalized.replace(/^page\s+\d+\s*(?:\|\s*page\s*\|\s*page\s+\d+\s*\|?)?/i, "").slice(0, 700);
@@ -4324,194 +4226,9 @@ function pageHeadingTitleFromText(text, fallback) {
4324
4226
  }
4325
4227
  return fallback;
4326
4228
  }
4327
- function pageFormTypeFromText(text) {
4328
- if (hasSubstantiveDeclarationsScheduleText(text)) return "declarations";
4329
- if (/\b(declarations?\s+page|declarations?\s+schedule)\b/i.test(text)) return "declarations";
4330
- if (/\b(endorsement\s+(?:no\.?|number|#)|this endorsement changes the policy|[A-Z]{2,}-END\s+\d{2,})\b/i.test(text)) return "endorsement";
4331
- if (/\b(technology errors?\s*&?\s*omissions.*liability insurance policy|policy form|coverage form|insuring agreement|definitions?|exclusions?|conditions?)\b/i.test(text)) return "coverage";
4332
- if (/\b(important notice|privacy notice|ofac advisory|terrorism risk insurance act|tria|trade or economic sanctions)\b/i.test(text)) return "notice";
4333
- return "other";
4334
- }
4335
4229
  function hasSubstantiveDeclarationsScheduleText(text) {
4336
4230
  return /\bitem\s+\d+\.?\s*(?:named insured|policy number|policy period|renewal|form of business|coverage parts?|limits?|premium|extended reporting|producer|forms? and endorsements?)\b/i.test(text) || /\bforms? and endorsements attached at inception\b/i.test(text) || /\bcoverage parts?,?\s+limits? of liability,?\s+deductibles?,?\s+and retroactive dates\b/i.test(text) || /\bannual premium\s*\(all coverage parts?\)\b/i.test(text) || /\berp option\b/i.test(text) || /\bproducer\b[\s\S]{0,240}\blicense\b/i.test(text);
4337
4231
  }
4338
- function administrativeFormTypeFromText(text) {
4339
- if (hasSubstantiveDeclarationsScheduleText(text)) return void 0;
4340
- if (/\b(important notice|privacy notice|ofac advisory|terrorism risk insurance act|tria|trade or economic sanctions|economic sanctions limitation|how to report a claim)\b/i.test(text)) {
4341
- return "notice";
4342
- }
4343
- if (/\b(specimen policy|policy jacket|countersigned|countersignature|licensed resident agent|corporate secretary|president and ceo|application of insurance executed)\b/i.test(text)) {
4344
- return "other";
4345
- }
4346
- return void 0;
4347
- }
4348
- function pageTextByNumber(sourceSpans) {
4349
- const pageTexts = /* @__PURE__ */ new Map();
4350
- const pageSpanTexts = /* @__PURE__ */ new Map();
4351
- for (const span of sourceSpans) {
4352
- const start = spanPageStart(span);
4353
- if (typeof start !== "number") continue;
4354
- const end = spanPageEnd(span) ?? start;
4355
- for (let page = start; page <= end; page += 1) {
4356
- if (spanSourceUnit(span) === "page") {
4357
- pageSpanTexts.set(page, cleanText(span.text, ""));
4358
- continue;
4359
- }
4360
- const existing = pageTexts.get(page) ?? "";
4361
- if (existing.length < 4e3) pageTexts.set(page, cleanText([existing, span.text].filter(Boolean).join(" "), ""));
4362
- }
4363
- }
4364
- return new Map([.../* @__PURE__ */ new Set([...pageTexts.keys(), ...pageSpanTexts.keys()])].map((page) => [
4365
- page,
4366
- pageSpanTexts.get(page) ?? pageTexts.get(page) ?? ""
4367
- ]));
4368
- }
4369
- function reconcileFormTypeWithSourceText(form, pageTexts) {
4370
- const pages = [];
4371
- const start = form.pageStart;
4372
- const end = form.pageEnd ?? start;
4373
- if (typeof start === "number" && typeof end === "number") {
4374
- for (let page = start; page <= end; page += 1) pages.push(pageTexts.get(page) ?? "");
4375
- }
4376
- const text = cleanText([form.title, form.formNumber, ...pages].filter(Boolean).join(" "), "");
4377
- const administrativeType = administrativeFormTypeFromText(text);
4378
- if (administrativeType && form.formType !== administrativeType) return administrativeType;
4379
- return form.formType;
4380
- }
4381
- function inferFormHintsFromSourceSpans(sourceSpans) {
4382
- const pageTexts = pageTextByNumber(sourceSpans);
4383
- if (pageTexts.size === 0) return [];
4384
- const pageHints = [...pageTexts.keys()].sort((left, right) => left - right).map((page) => {
4385
- const text = pageTexts.get(page) ?? "";
4386
- const formNumber = formNumberFromText(text);
4387
- return {
4388
- formNumber,
4389
- editionDate: editionDateFromFormNumber(formNumber),
4390
- title: pageTitleFromText(text, `Page ${page}`),
4391
- formType: pageFormTypeFromText(text),
4392
- pageStart: page,
4393
- pageEnd: page
4394
- };
4395
- });
4396
- const merged = [];
4397
- for (const hint of pageHints) {
4398
- const previous = merged[merged.length - 1];
4399
- const startsNewEndorsement = hint.formType === "endorsement" && /\bendorsement\s+(?:no\.?|number|#)\s*[A-Z0-9]|this endorsement changes the policy/i.test(hint.title ?? "");
4400
- const canMerge = previous && previous.formType === hint.formType && previous.pageEnd !== void 0 && hint.pageStart === previous.pageEnd + 1 && (hint.formType === "declarations" || hint.formType === "coverage" || hint.formType === "endorsement" && !startsNewEndorsement);
4401
- if (!canMerge) {
4402
- merged.push(hint);
4403
- continue;
4404
- }
4405
- previous.pageEnd = hint.pageEnd;
4406
- previous.title = previous.title ?? hint.title;
4407
- previous.formNumber = previous.formNumber ?? hint.formNumber;
4408
- previous.editionDate = previous.editionDate ?? hint.editionDate;
4409
- }
4410
- return merged;
4411
- }
4412
- function normalizeFormHints(forms, sourceSpans) {
4413
- const pageTexts = pageTextByNumber(sourceSpans);
4414
- const provided = (forms ?? []).filter(
4415
- (form) => typeof form.pageStart === "number" && typeof form.pageEnd === "number" && form.pageStart > 0 && form.pageEnd >= form.pageStart
4416
- ).map((form) => ({
4417
- ...form,
4418
- formType: reconcileFormTypeWithSourceText(form, pageTexts),
4419
- title: form.title ? cleanText(form.title, "") : void 0
4420
- })).sort(
4421
- (left, right) => (left.pageStart ?? Number.MAX_SAFE_INTEGER) - (right.pageStart ?? Number.MAX_SAFE_INTEGER) || (left.pageEnd ?? Number.MAX_SAFE_INTEGER) - (right.pageEnd ?? Number.MAX_SAFE_INTEGER)
4422
- );
4423
- return provided.length ? provided : inferFormHintsFromSourceSpans(sourceSpans);
4424
- }
4425
- function formHintForPage(forms, page) {
4426
- return forms.find(
4427
- (form) => typeof form.pageStart === "number" && typeof form.pageEnd === "number" && page >= form.pageStart && page <= form.pageEnd
4428
- );
4429
- }
4430
- function titleFromFormHint(form, fallback) {
4431
- if (form.formType === "declarations") return "Declarations";
4432
- if (form.formType === "coverage") return "Policy Form";
4433
- if (form.formType === "endorsement") return endorsementTitle([form.title, form.formNumber].filter(Boolean).join(" ")) ?? cleanText(form.title, fallback);
4434
- return cleanText(form.title, fallback);
4435
- }
4436
- function formGroupConfig(form) {
4437
- if (form.formType === "declarations") {
4438
- return {
4439
- kind: "page_group",
4440
- title: "Declarations",
4441
- description: "Declarations pages and schedules grouped from form inventory",
4442
- organizer: "form_inventory_declarations_grouping"
4443
- };
4444
- }
4445
- if (form.formType === "coverage") {
4446
- return {
4447
- kind: "form",
4448
- title: "Policy Form",
4449
- description: "Policy form pages grouped from form inventory",
4450
- organizer: "form_inventory_policy_form_grouping"
4451
- };
4452
- }
4453
- if (form.formType === "endorsement") {
4454
- const title = titleFromFormHint(form, "Endorsement");
4455
- return {
4456
- kind: "endorsement",
4457
- title,
4458
- description: `${title} grouped from form inventory`,
4459
- organizer: "form_inventory_endorsement_grouping"
4460
- };
4461
- }
4462
- return void 0;
4463
- }
4464
- function applyFormInventoryHints(sourceTree, forms) {
4465
- if (forms.length === 0) return sourceTree;
4466
- const rootId = sourceTreeRootId(sourceTree);
4467
- if (!rootId) return sourceTree;
4468
- const byParent = nodesByParent(sourceTree);
4469
- const children = (byParent.get(rootId) ?? []).filter((node) => node.kind !== "document").sort((left, right) => left.order - right.order);
4470
- const rootPages = children.filter((node) => node.kind === "page" && typeof node.pageStart === "number");
4471
- let nextTree = sourceTree.map((node) => {
4472
- if (node.kind !== "page" || typeof node.pageStart !== "number") return node;
4473
- const form = formHintForPage(forms, node.pageStart);
4474
- if (!form) return node;
4475
- const isStartPage = form.pageStart === node.pageStart;
4476
- const shouldRetitle = isStartPage && form.formType !== "other";
4477
- return {
4478
- ...node,
4479
- title: shouldRetitle ? titleFromFormHint(form, node.title) : node.title,
4480
- metadata: {
4481
- ...node.metadata,
4482
- formInventoryHint: {
4483
- formType: form.formType,
4484
- formNumber: form.formNumber,
4485
- title: form.title,
4486
- pageStart: form.pageStart,
4487
- pageEnd: form.pageEnd
4488
- }
4489
- }
4490
- };
4491
- });
4492
- const claimed = /* @__PURE__ */ new Set();
4493
- for (const form of forms) {
4494
- const config = formGroupConfig(form);
4495
- const pageStart2 = form.pageStart;
4496
- const pageEnd2 = form.pageEnd;
4497
- if (!config || typeof pageStart2 !== "number" || typeof pageEnd2 !== "number") continue;
4498
- const childIds = rootPages.filter(
4499
- (page) => !claimed.has(page.id) && typeof page.pageStart === "number" && page.pageStart >= pageStart2 && page.pageStart <= pageEnd2
4500
- ).map((page) => page.id);
4501
- if (childIds.length === 0) continue;
4502
- nextTree = groupAdjacentChildren({
4503
- sourceTree: nextTree,
4504
- children,
4505
- childIds,
4506
- kind: config.kind,
4507
- title: config.title,
4508
- description: config.description,
4509
- organizer: config.organizer
4510
- });
4511
- childIds.forEach((id) => claimed.add(id));
4512
- }
4513
- return normalizeDocumentSourceTreePaths(nextTree);
4514
- }
4515
4232
  function looksLikeDeclarationsStart(node) {
4516
4233
  const title = cleanText(node.title, "");
4517
4234
  const text = sourceNodeText(node);
@@ -4522,13 +4239,13 @@ function looksLikeDeclarationsStart(node) {
4522
4239
  }
4523
4240
  function looksLikeDeclarationsContinuation(node) {
4524
4241
  const text = sourceNodeText(node);
4525
- return looksLikeDeclarationsStart(node) || /\b(item\s+\d+\.|coverage part|each claim limit|aggregate limit|retroactive date|self-insured retention|premium|payment plan|producer|broker|forms? and endorsements?|extended reporting period|discovery period)\b/i.test(text);
4242
+ return looksLikeDeclarationsStart(node) || /\b(item\s+\d+\.|coverage part|limits?,?\s+sub-limits?|each claim limit|aggregate limit|retroactive date|self-insured retention|premium|payment plan|producer|broker|forms? and endorsements?|attached at inception|extended reporting period|discovery period)\b/i.test(text) || /\b(these declarations|policy form|[A-Z]{2,}-END\s+\d{3}|endorsement\s+(?:no\.?|number|#)?\s*\d+)\b/i.test(text);
4526
4243
  }
4527
4244
  function looksLikePolicyFormStart(node) {
4528
4245
  const text = sourceNodeText(node);
4529
4246
  const excerpt = cleanText(node.textExcerpt, "");
4530
4247
  if (isAdministrativeNoticeNode(node) || looksLikeDeclarationsStart(node)) return false;
4531
- return /\bpolicy form\b/i.test(node.title) || /^policy\s+form\b/i.test(excerpt) || /\btechnology errors?\s*&?\s*omissions\b/i.test(text) && /\bplease read this entire policy carefully\b/i.test(text) || /\bform\s+[A-Z]{2,}-[A-Z0-9-]+\s+\d{2}\s+\d{2}\b/i.test(text);
4248
+ return /\bpolicy form\b/i.test(node.title) || /^policy\s+form\b/i.test(excerpt) || /\btechnology errors?\s*&?\s*omissions\b/i.test(text) && /\bplease read this entire policy carefully\b/i.test(text) || /\bsection\s+[IVX0-9]+\s*[—-]\s*(insuring agreements?|definitions?|exclusions?|conditions?)\b/i.test(text) || /\bform\s+[A-Z]{2,}-[A-Z0-9-]+\s+\d{2}\s+\d{2}\b/i.test(text);
4532
4249
  }
4533
4250
  function looksLikePolicyFormContinuation(node) {
4534
4251
  const text = sourceNodeText(node);
@@ -5290,25 +5007,51 @@ function isOperationalEvidenceAnchor(span) {
5290
5007
  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;
5291
5008
  return false;
5292
5009
  }
5010
+ function operationalEvidencePages(sourceSpans) {
5011
+ const scores = /* @__PURE__ */ new Map();
5012
+ for (const span of sourceSpans) {
5013
+ const page = spanPageStart(span);
5014
+ if (typeof page !== "number") continue;
5015
+ const text = cleanText([span.text, span.formNumber, spanSourceUnit(span)].filter(Boolean).join(" "), "");
5016
+ if (!text) continue;
5017
+ let score = Math.max(0, operationalEvidenceScore(span));
5018
+ if (/\bdeclarations?\s+(page|schedule)?\b/i.test(text)) score += 24;
5019
+ if (/\bitem\s+\d+\.?\s*(?:named insured|policy number|policy period|coverage parts?|limits?|premium|producer|forms? and endorsements?)\b/i.test(text)) score += 20;
5020
+ if (/\bcoverage parts?,?\s+limits? of liability,?\s+deductibles?,?\s+and retroactive dates\b/i.test(text)) score += 24;
5021
+ if (/\b(policy\s*(number|period|term)|effective date|expiration date|expiry date|named insured|insurer|carrier|security)\b/i.test(text)) score += 12;
5022
+ if (/\b(premium|total due|tax|fee|producer|broker)\b/i.test(text)) score += 10;
5023
+ if (/\b(endorsement\s+(?:no\.?|number|#)?\s*[A-Z0-9]|attached at inception|forms? and endorsements?)\b/i.test(text)) score += 8;
5024
+ if (/\b(definitions?|exclusions?|conditions?|duties in the event|action against|cancellation by)\b/i.test(text)) score -= 12;
5025
+ if (score > 0) scores.set(page, (scores.get(page) ?? 0) + score);
5026
+ }
5027
+ return new Set(
5028
+ [...scores.entries()].filter(([, score]) => score >= 18).sort((left, right) => right[1] - left[1] || left[0] - right[0]).slice(0, 12).map(([page]) => page)
5029
+ );
5030
+ }
5293
5031
  function operationalProfileEvidence(sourceTree, sourceSpans) {
5294
5032
  const sorted = [...sourceSpans].sort(
5295
5033
  (left, right) => (spanPageStart(left) ?? Number.MAX_SAFE_INTEGER) - (spanPageStart(right) ?? Number.MAX_SAFE_INTEGER) || (left.location?.charStart ?? Number.MAX_SAFE_INTEGER) - (right.location?.charStart ?? Number.MAX_SAFE_INTEGER) || left.id.localeCompare(right.id)
5296
5034
  );
5035
+ const selectedPages = operationalEvidencePages(sorted);
5297
5036
  const selected = /* @__PURE__ */ new Set();
5298
5037
  const selectedTableIds = /* @__PURE__ */ new Set();
5299
5038
  for (let index = 0; index < sorted.length; index += 1) {
5300
- const score = operationalEvidenceScore(sorted[index]);
5039
+ const span = sorted[index];
5040
+ const score = operationalEvidenceScore(span);
5301
5041
  if (score < 8) continue;
5302
- if (!isOperationalEvidenceAnchor(sorted[index])) continue;
5303
- const tableId2 = spanTableId(sorted[index]);
5304
- if (tableId2 && !isTableCellSpan(sorted[index])) selectedTableIds.add(tableId2);
5305
- const page = spanPageStart(sorted[index]);
5306
- for (let offset = -2; offset <= 2; offset += 1) {
5042
+ if (!isOperationalEvidenceAnchor(span)) continue;
5043
+ const page = spanPageStart(span);
5044
+ if (selectedPages.size > 0 && typeof page === "number" && !selectedPages.has(page) && score < 30) continue;
5045
+ const tableId2 = spanTableId(span);
5046
+ if (tableId2 && !isTableCellSpan(span)) selectedTableIds.add(tableId2);
5047
+ const neighborWindow = score >= 24 ? 2 : 1;
5048
+ for (let offset = -neighborWindow; offset <= neighborWindow; offset += 1) {
5307
5049
  const neighborIndex = index + offset;
5308
5050
  const neighbor = sorted[neighborIndex];
5309
5051
  if (!neighbor || spanPageStart(neighbor) !== page) continue;
5052
+ if (selectedPages.size > 0 && typeof page === "number" && !selectedPages.has(page) && operationalEvidenceScore(neighbor) < 30) continue;
5310
5053
  const neighborText = cleanText(neighbor.text, "");
5311
- if (!neighborText || neighborText.length > 5e3) continue;
5054
+ if (!neighborText || neighborText.length > 3e3) continue;
5312
5055
  selected.add(neighborIndex);
5313
5056
  }
5314
5057
  }
@@ -5317,7 +5060,7 @@ function operationalProfileEvidence(sourceTree, sourceSpans) {
5317
5060
  const tableId2 = spanTableId(span);
5318
5061
  if (!tableId2 || !selectedTableIds.has(tableId2) || isTableCellSpan(span)) return;
5319
5062
  const text = cleanText(span.text, "");
5320
- if (text && text.length <= 5e3) selected.add(index);
5063
+ if (text && text.length <= 3e3) selected.add(index);
5321
5064
  });
5322
5065
  }
5323
5066
  const nodeIdsBySpanId = sourceNodeIdsBySpanId(sourceTree);
@@ -5335,12 +5078,12 @@ function operationalProfileEvidence(sourceTree, sourceSpans) {
5335
5078
  pageEnd: spanPageEnd(span),
5336
5079
  sourceUnit: spanSourceUnit(span),
5337
5080
  formNumber: span.formNumber,
5338
- text: text.slice(0, span.sourceUnit === "page" ? 1200 : 900)
5081
+ text: text.slice(0, span.sourceUnit === "page" ? 900 : 700)
5339
5082
  }];
5340
5083
  });
5341
5084
  const detailEntries = entries.filter((entry) => entry.sourceUnit !== "page");
5342
5085
  const pageEntries = entries.filter((entry) => entry.sourceUnit === "page");
5343
- return [...detailEntries, ...pageEntries].slice(0, 180);
5086
+ return [...detailEntries.slice(0, 80), ...pageEntries.slice(0, 8)];
5344
5087
  }
5345
5088
  function sourceTreeRootId(sourceTree) {
5346
5089
  return sourceTree.find((node) => node.kind === "document")?.id;
@@ -5388,7 +5131,8 @@ Rules:
5388
5131
  - 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.
5389
5132
  - 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.
5390
5133
  - 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.
5391
- - Forms-and-endorsements schedules are operational form inventory evidence, not coverage limits. Do not turn form schedule rows into coverage units unless the row also states a coverage-specific limit or deductible.
5134
+ - 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.
5135
+ - Forms-and-endorsements schedules are form schedule evidence, not coverage limits. Do not turn form schedule rows into coverage units unless the row also states a coverage-specific limit or deductible.
5392
5136
  - Keep each coverage unit tied to one evidence scope: a declaration/core schedule row, a core policy form section, or one specific endorsement schedule. Do not merge declaration facts and endorsement schedule facts into the same coverage unit, even when they use the same coverage name.
5393
5137
  - If the declarations schedule and an endorsement schedule both list Network Security, Social Engineering Fraud, Regulatory Proceedings, or another same-named coverage, return separate coverage units for each supported source scope.
5394
5138
  - Use the declaration coverage name for declaration/core schedule rows. Use the endorsement title or endorsement schedule coverage name for endorsement rows, and include formNumber and endorsementNumber when source-backed.
@@ -5627,8 +5371,8 @@ async function cleanupOperationalCoverageSchedules(params) {
5627
5371
  }
5628
5372
  async function runSourceTreeExtraction(params) {
5629
5373
  const sourceSpans = normalizeSourceSpans(params.sourceSpans);
5630
- const formHints = normalizeFormHints(params.formInventory?.forms, sourceSpans);
5631
- let sourceTree = applySemanticPageGrouping(applyFormInventoryHints(buildDocumentSourceTree(sourceSpans, params.id), formHints));
5374
+ const formHints = [];
5375
+ let sourceTree = applySemanticPageGrouping(buildDocumentSourceTree(sourceSpans, params.id));
5632
5376
  const warnings = [];
5633
5377
  let modelCalls = 0;
5634
5378
  let callsWithUsage = 0;