@claritylabs/cl-sdk 3.2.9 → 3.2.11
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/README.md +6 -8
- package/dist/index.js +51 -232
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +51 -232
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -37,7 +37,6 @@ const extractor = createExtractor({
|
|
|
37
37
|
return { object: result.object, usage: result.usage };
|
|
38
38
|
},
|
|
39
39
|
concurrency: 3,
|
|
40
|
-
pageMapConcurrency: 3,
|
|
41
40
|
extractorConcurrency: 4,
|
|
42
41
|
formatConcurrency: 2,
|
|
43
42
|
reviewMode: "auto",
|
|
@@ -81,7 +80,7 @@ const extractor = createExtractor({ generateText, generateObject, sourceStore })
|
|
|
81
80
|
const result = await extractor.extract(pdfBase64, "policy-123", { sourceSpans });
|
|
82
81
|
```
|
|
83
82
|
|
|
84
|
-
When source spans are available, extraction returns `sourceTree`, `sourceSpans`, `operationalProfile`, `warnings`, `tokenUsage`, and `performanceReport`. The source tree is canonical for policy wording and hierarchy. The extractor
|
|
83
|
+
When source spans are available, extraction returns `sourceTree`, `sourceSpans`, `operationalProfile`, `warnings`, `tokenUsage`, and `performanceReport`. The source tree is canonical for policy wording and hierarchy. The extractor groups parser-provided page, title, table, row, and text spans into declarations, policy forms, endorsements, sections, and schedules without running a separate form-inventory, page-map, or source-tree-organizer model call. The operational profile model receives a bounded packet of high-value declaration, schedule, premium, producer, endorsement, and coverage evidence rather than the full document wording. The operational profile contains model-extracted policy metadata, parties, coverage units, nested coverage limit terms, deductibles/retentions, premiums, key dates, and endorsement-support facts, each with `sourceNodeIds` and `sourceSpanIds`. Source-backed identity records such as insurer, producer, contacts, named insured rows, endorsement parties, and standalone insured addresses must include non-empty `sourceSpanIds`. After extraction, a bounded model cleanup pass may keep, drop, or update existing coverage rows and terms by index; the SDK enforces the schema and rejects source IDs that are not present in the current source tree/spans. Endorsement schedules are modeled as whole endorsement coverage units with their own `limits[]` terms instead of unrelated flat rows like `Aggregate Limit`.
|
|
85
84
|
|
|
86
85
|
Store `result.sourceTree` in a retrievable node index and embed node `description` values for search. Keep `result.sourceSpans` as the exact PDF highlighting layer. `result.document` and its `documentOutline` remain compatibility projections for existing host screens; do not treat broad structured policy JSON as canonical extraction truth.
|
|
87
86
|
|
|
@@ -140,17 +139,16 @@ If your callback ignores those fields, the model will only see the text prompt.
|
|
|
140
139
|
|
|
141
140
|
## Model routing metadata
|
|
142
141
|
|
|
143
|
-
Every SDK model callback may receive `taskKind`, `budgetDiagnostics`, and `trace`. Hosts can use these provider-agnostic fields for cheap-first routing, fallback, and telemetry without the SDK hardcoding model names. Example task kinds include `
|
|
142
|
+
Every SDK model callback may receive `taskKind`, `budgetDiagnostics`, and `trace`. Hosts can use these provider-agnostic fields for cheap-first routing, fallback, and telemetry without the SDK hardcoding model names. Example task kinds include `extraction_operational_profile`, `extraction_coverage_cleanup`, `query_reason`, `application_extract_fields`, and `pce_impact_analysis`. `budgetDiagnostics` includes the resolved output-token budget, preferred task budget, truncation-risk warnings, and any model/provider caps that constrained the current subtask. `maxOutputTokens` is treated as a ceiling, not a default request size, so small classifier and lookup calls stay small even on models with large output windows. `trace` identifies the current source-backed or workflow call so host logs can show what was being generated instead of a generic model-call label.
|
|
144
143
|
|
|
145
144
|
## Bounded Agentic Workflows
|
|
146
145
|
|
|
147
146
|
CL-SDK uses deterministic scaffolding with agentic decision points rather than fixed all-tools-all-the-time chains:
|
|
148
147
|
|
|
149
|
-
-
|
|
150
|
-
-
|
|
151
|
-
- Referential coverage resolution
|
|
152
|
-
-
|
|
153
|
-
- Formatting skips the LLM cleanup pass for plain prose and formats long or noisy markdown/table/list content in parallel batches.
|
|
148
|
+
- Policy extraction requires caller-provided source spans and does not run the older form-inventory, page-map, focused-extractor, or source-tree-organizer model calls.
|
|
149
|
+
- Source-tree construction stays deterministic from parser spans; the model is used for the operational profile and optional coverage cleanup because those are end-user facts.
|
|
150
|
+
- Referential coverage resolution and application/query workflows still use bounded target-specific actions when those workflows need lookup or explanation steps.
|
|
151
|
+
- Formatting skips the LLM cleanup pass for plain prose and formats long or noisy markdown/table/list content in parallel batches outside the v3 source-span extraction path.
|
|
154
152
|
- `reviewMode: "auto"` skips the expensive LLM review pass when deterministic checks are clean and source spans are available. Use `"always"` for maximum review coverage or `"skip"` when the host owns quality review separately.
|
|
155
153
|
- Application processing plans optional backfill, context auto-fill, document search, batching, reply parsing, lookup, explanations, and next-batch email generation based on current active question state. Conditional fields that are not active are skipped until their parent answers trigger them.
|
|
156
154
|
|
package/dist/index.js
CHANGED
|
@@ -4149,9 +4149,11 @@ Task:
|
|
|
4149
4149
|
Projection defects to look for:
|
|
4150
4150
|
- Generic labels such as "Column 3" that should be renamed from nearby row/header evidence.
|
|
4151
4151
|
- Declaration or section headers projected as coverage names when the row evidence is actually a specific coverage, sub-limit, deductible, retention, retroactive date, or premium.
|
|
4152
|
+
- Premium-only, tax-only, fee-only, rating, exposure, reporting-value, or payment-plan rows projected as coverage rows.
|
|
4152
4153
|
- Dangling continuation punctuation such as a trailing "/" copied into values.
|
|
4153
4154
|
- Item references such as "shown in Item 7" or bare item numbers treated as money amounts.
|
|
4154
4155
|
- Policy wording, exclusions, or unsupported prose copied into operational limit/deductible fields.
|
|
4156
|
+
- Premium, MGA Fee, taxes, stamping fees, total premium, total due, reporting values, or exposure annual rate used as a coverage limit.
|
|
4155
4157
|
- Header/value splits where "Limit of Liability", "Deductible", "Retroactive Date", "Aggregate", "Each Claim", or similar terms are attached to the wrong coverage row.
|
|
4156
4158
|
- Repeated schedule headings projected as separate coverages when they only introduce the next coverage group.
|
|
4157
4159
|
|
|
@@ -4161,7 +4163,8 @@ Rules:
|
|
|
4161
4163
|
- Use sourceNodeIds and sourceSpanIds only from the provided source nodes or from the existing candidate entry.
|
|
4162
4164
|
- Prefer dropping a malformed fact over speculative rewriting.
|
|
4163
4165
|
- Keep a coverage when it is a real operational coverage/benefit even if only one term needs cleanup.
|
|
4164
|
-
-
|
|
4166
|
+
- Drop a coverage row when its only facts are premium, tax, fee, rating, reporting-value, exposure, or payment-plan facts and it has no source-backed limit, deductible, retention, retroactive date, sublimit, or benefit term.
|
|
4167
|
+
- Never drop a declaration or schedule coverage row that names a coverage and states policy-specific limits, dates, deductibles, retentions, sublimits, or benefit terms. Repair its terms instead.
|
|
4165
4168
|
- When changing a term's semantic meaning, set kind to the corrected normalized term kind.
|
|
4166
4169
|
- Do not add new coverage rows or new terms; this pass cleans the existing projection.
|
|
4167
4170
|
- 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.
|
|
@@ -4583,36 +4586,6 @@ function spanPageEnd(span) {
|
|
|
4583
4586
|
function spanSourceUnit(span) {
|
|
4584
4587
|
return span.sourceUnit ?? span.metadata?.sourceUnit ?? span.metadata?.elementType;
|
|
4585
4588
|
}
|
|
4586
|
-
function formNumberFromText(value) {
|
|
4587
|
-
return cleanText(value, "").match(/\b[A-Z]{2,}(?:-[A-Z0-9]+)+\s+\d{2}\s+\d{2}\b/)?.[0]?.replace(/\s+/g, " ");
|
|
4588
|
-
}
|
|
4589
|
-
function editionDateFromFormNumber(formNumber) {
|
|
4590
|
-
const match = formNumber?.match(/\b(\d{2})\s+(\d{2})$/);
|
|
4591
|
-
return match ? `${match[1]}/${match[2]}` : void 0;
|
|
4592
|
-
}
|
|
4593
|
-
function pageTitleFromText(text, fallback) {
|
|
4594
|
-
const normalized = cleanText(text, fallback);
|
|
4595
|
-
const patterns = [
|
|
4596
|
-
/\bIMPORTANT NOTICE\s+[—-]\s+HOW TO REPORT A CLAIM\b/i,
|
|
4597
|
-
/\bPRIVACY NOTICE TO POLICYHOLDERS\b/i,
|
|
4598
|
-
/\bOFAC ADVISORY NOTICE\b/i,
|
|
4599
|
-
/\bTERRORISM RISK INSURANCE ACT\s*\(TRIA\)\s*DISCLOSURE AND REJECTION\b/i,
|
|
4600
|
-
/\bDECLARATIONS PAGE\b/i,
|
|
4601
|
-
/\bTECHNOLOGY ERRORS?\s*&\s*OMISSIONS AND CYBER LIABILITY INSURANCE POLICY\b/i,
|
|
4602
|
-
/\bTRADE OR ECONOMIC SANCTIONS LIMITATION\b/i,
|
|
4603
|
-
/\bFORMS? AND ENDORSEMENTS\b/i
|
|
4604
|
-
];
|
|
4605
|
-
for (const pattern of patterns) {
|
|
4606
|
-
const match = normalized.match(pattern)?.[0];
|
|
4607
|
-
if (match) return cleanText(match, fallback);
|
|
4608
|
-
}
|
|
4609
|
-
const endorsement = normalized.match(/\bENDORSEMENT\s+(?:NO\.?|NUMBER|#)\s*[A-Z0-9][A-Z0-9.-]*\b/i)?.[0];
|
|
4610
|
-
if (endorsement) return cleanText(endorsement, fallback);
|
|
4611
|
-
const firstSentence = normalized.split(/(?<=\.)\s+/)[0];
|
|
4612
|
-
if (/^page\s+\d+\b/i.test(firstSentence)) return fallback;
|
|
4613
|
-
if (firstSentence && firstSentence.length <= 120) return firstSentence.replace(/[.]$/, "");
|
|
4614
|
-
return fallback;
|
|
4615
|
-
}
|
|
4616
4589
|
function pageHeadingTitleFromText(text, fallback) {
|
|
4617
4590
|
const normalized = cleanText(text, "");
|
|
4618
4591
|
const headingText = normalized.replace(/^page\s+\d+\s*(?:\|\s*page\s*\|\s*page\s+\d+\s*\|?)?/i, "").slice(0, 700);
|
|
@@ -4632,194 +4605,9 @@ function pageHeadingTitleFromText(text, fallback) {
|
|
|
4632
4605
|
}
|
|
4633
4606
|
return fallback;
|
|
4634
4607
|
}
|
|
4635
|
-
function pageFormTypeFromText(text) {
|
|
4636
|
-
if (hasSubstantiveDeclarationsScheduleText(text)) return "declarations";
|
|
4637
|
-
if (/\b(declarations?\s+page|declarations?\s+schedule)\b/i.test(text)) return "declarations";
|
|
4638
|
-
if (/\b(endorsement\s+(?:no\.?|number|#)|this endorsement changes the policy|[A-Z]{2,}-END\s+\d{2,})\b/i.test(text)) return "endorsement";
|
|
4639
|
-
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";
|
|
4640
|
-
if (/\b(important notice|privacy notice|ofac advisory|terrorism risk insurance act|tria|trade or economic sanctions)\b/i.test(text)) return "notice";
|
|
4641
|
-
return "other";
|
|
4642
|
-
}
|
|
4643
4608
|
function hasSubstantiveDeclarationsScheduleText(text) {
|
|
4644
4609
|
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);
|
|
4645
4610
|
}
|
|
4646
|
-
function administrativeFormTypeFromText(text) {
|
|
4647
|
-
if (hasSubstantiveDeclarationsScheduleText(text)) return void 0;
|
|
4648
|
-
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)) {
|
|
4649
|
-
return "notice";
|
|
4650
|
-
}
|
|
4651
|
-
if (/\b(specimen policy|policy jacket|countersigned|countersignature|licensed resident agent|corporate secretary|president and ceo|application of insurance executed)\b/i.test(text)) {
|
|
4652
|
-
return "other";
|
|
4653
|
-
}
|
|
4654
|
-
return void 0;
|
|
4655
|
-
}
|
|
4656
|
-
function pageTextByNumber(sourceSpans) {
|
|
4657
|
-
const pageTexts = /* @__PURE__ */ new Map();
|
|
4658
|
-
const pageSpanTexts = /* @__PURE__ */ new Map();
|
|
4659
|
-
for (const span of sourceSpans) {
|
|
4660
|
-
const start = spanPageStart(span);
|
|
4661
|
-
if (typeof start !== "number") continue;
|
|
4662
|
-
const end = spanPageEnd(span) ?? start;
|
|
4663
|
-
for (let page = start; page <= end; page += 1) {
|
|
4664
|
-
if (spanSourceUnit(span) === "page") {
|
|
4665
|
-
pageSpanTexts.set(page, cleanText(span.text, ""));
|
|
4666
|
-
continue;
|
|
4667
|
-
}
|
|
4668
|
-
const existing = pageTexts.get(page) ?? "";
|
|
4669
|
-
if (existing.length < 4e3) pageTexts.set(page, cleanText([existing, span.text].filter(Boolean).join(" "), ""));
|
|
4670
|
-
}
|
|
4671
|
-
}
|
|
4672
|
-
return new Map([.../* @__PURE__ */ new Set([...pageTexts.keys(), ...pageSpanTexts.keys()])].map((page) => [
|
|
4673
|
-
page,
|
|
4674
|
-
pageSpanTexts.get(page) ?? pageTexts.get(page) ?? ""
|
|
4675
|
-
]));
|
|
4676
|
-
}
|
|
4677
|
-
function reconcileFormTypeWithSourceText(form, pageTexts) {
|
|
4678
|
-
const pages = [];
|
|
4679
|
-
const start = form.pageStart;
|
|
4680
|
-
const end = form.pageEnd ?? start;
|
|
4681
|
-
if (typeof start === "number" && typeof end === "number") {
|
|
4682
|
-
for (let page = start; page <= end; page += 1) pages.push(pageTexts.get(page) ?? "");
|
|
4683
|
-
}
|
|
4684
|
-
const text = cleanText([form.title, form.formNumber, ...pages].filter(Boolean).join(" "), "");
|
|
4685
|
-
const administrativeType = administrativeFormTypeFromText(text);
|
|
4686
|
-
if (administrativeType && form.formType !== administrativeType) return administrativeType;
|
|
4687
|
-
return form.formType;
|
|
4688
|
-
}
|
|
4689
|
-
function inferFormHintsFromSourceSpans(sourceSpans) {
|
|
4690
|
-
const pageTexts = pageTextByNumber(sourceSpans);
|
|
4691
|
-
if (pageTexts.size === 0) return [];
|
|
4692
|
-
const pageHints = [...pageTexts.keys()].sort((left, right) => left - right).map((page) => {
|
|
4693
|
-
const text = pageTexts.get(page) ?? "";
|
|
4694
|
-
const formNumber = formNumberFromText(text);
|
|
4695
|
-
return {
|
|
4696
|
-
formNumber,
|
|
4697
|
-
editionDate: editionDateFromFormNumber(formNumber),
|
|
4698
|
-
title: pageTitleFromText(text, `Page ${page}`),
|
|
4699
|
-
formType: pageFormTypeFromText(text),
|
|
4700
|
-
pageStart: page,
|
|
4701
|
-
pageEnd: page
|
|
4702
|
-
};
|
|
4703
|
-
});
|
|
4704
|
-
const merged = [];
|
|
4705
|
-
for (const hint of pageHints) {
|
|
4706
|
-
const previous = merged[merged.length - 1];
|
|
4707
|
-
const startsNewEndorsement = hint.formType === "endorsement" && /\bendorsement\s+(?:no\.?|number|#)\s*[A-Z0-9]|this endorsement changes the policy/i.test(hint.title ?? "");
|
|
4708
|
-
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);
|
|
4709
|
-
if (!canMerge) {
|
|
4710
|
-
merged.push(hint);
|
|
4711
|
-
continue;
|
|
4712
|
-
}
|
|
4713
|
-
previous.pageEnd = hint.pageEnd;
|
|
4714
|
-
previous.title = previous.title ?? hint.title;
|
|
4715
|
-
previous.formNumber = previous.formNumber ?? hint.formNumber;
|
|
4716
|
-
previous.editionDate = previous.editionDate ?? hint.editionDate;
|
|
4717
|
-
}
|
|
4718
|
-
return merged;
|
|
4719
|
-
}
|
|
4720
|
-
function normalizeFormHints(forms, sourceSpans) {
|
|
4721
|
-
const pageTexts = pageTextByNumber(sourceSpans);
|
|
4722
|
-
const provided = (forms ?? []).filter(
|
|
4723
|
-
(form) => typeof form.pageStart === "number" && typeof form.pageEnd === "number" && form.pageStart > 0 && form.pageEnd >= form.pageStart
|
|
4724
|
-
).map((form) => ({
|
|
4725
|
-
...form,
|
|
4726
|
-
formType: reconcileFormTypeWithSourceText(form, pageTexts),
|
|
4727
|
-
title: form.title ? cleanText(form.title, "") : void 0
|
|
4728
|
-
})).sort(
|
|
4729
|
-
(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)
|
|
4730
|
-
);
|
|
4731
|
-
return provided.length ? provided : inferFormHintsFromSourceSpans(sourceSpans);
|
|
4732
|
-
}
|
|
4733
|
-
function formHintForPage(forms, page) {
|
|
4734
|
-
return forms.find(
|
|
4735
|
-
(form) => typeof form.pageStart === "number" && typeof form.pageEnd === "number" && page >= form.pageStart && page <= form.pageEnd
|
|
4736
|
-
);
|
|
4737
|
-
}
|
|
4738
|
-
function titleFromFormHint(form, fallback) {
|
|
4739
|
-
if (form.formType === "declarations") return "Declarations";
|
|
4740
|
-
if (form.formType === "coverage") return "Policy Form";
|
|
4741
|
-
if (form.formType === "endorsement") return endorsementTitle([form.title, form.formNumber].filter(Boolean).join(" ")) ?? cleanText(form.title, fallback);
|
|
4742
|
-
return cleanText(form.title, fallback);
|
|
4743
|
-
}
|
|
4744
|
-
function formGroupConfig(form) {
|
|
4745
|
-
if (form.formType === "declarations") {
|
|
4746
|
-
return {
|
|
4747
|
-
kind: "page_group",
|
|
4748
|
-
title: "Declarations",
|
|
4749
|
-
description: "Declarations pages and schedules grouped from form inventory",
|
|
4750
|
-
organizer: "form_inventory_declarations_grouping"
|
|
4751
|
-
};
|
|
4752
|
-
}
|
|
4753
|
-
if (form.formType === "coverage") {
|
|
4754
|
-
return {
|
|
4755
|
-
kind: "form",
|
|
4756
|
-
title: "Policy Form",
|
|
4757
|
-
description: "Policy form pages grouped from form inventory",
|
|
4758
|
-
organizer: "form_inventory_policy_form_grouping"
|
|
4759
|
-
};
|
|
4760
|
-
}
|
|
4761
|
-
if (form.formType === "endorsement") {
|
|
4762
|
-
const title = titleFromFormHint(form, "Endorsement");
|
|
4763
|
-
return {
|
|
4764
|
-
kind: "endorsement",
|
|
4765
|
-
title,
|
|
4766
|
-
description: `${title} grouped from form inventory`,
|
|
4767
|
-
organizer: "form_inventory_endorsement_grouping"
|
|
4768
|
-
};
|
|
4769
|
-
}
|
|
4770
|
-
return void 0;
|
|
4771
|
-
}
|
|
4772
|
-
function applyFormInventoryHints(sourceTree, forms) {
|
|
4773
|
-
if (forms.length === 0) return sourceTree;
|
|
4774
|
-
const rootId = sourceTreeRootId(sourceTree);
|
|
4775
|
-
if (!rootId) return sourceTree;
|
|
4776
|
-
const byParent = nodesByParent(sourceTree);
|
|
4777
|
-
const children = (byParent.get(rootId) ?? []).filter((node) => node.kind !== "document").sort((left, right) => left.order - right.order);
|
|
4778
|
-
const rootPages = children.filter((node) => node.kind === "page" && typeof node.pageStart === "number");
|
|
4779
|
-
let nextTree = sourceTree.map((node) => {
|
|
4780
|
-
if (node.kind !== "page" || typeof node.pageStart !== "number") return node;
|
|
4781
|
-
const form = formHintForPage(forms, node.pageStart);
|
|
4782
|
-
if (!form) return node;
|
|
4783
|
-
const isStartPage = form.pageStart === node.pageStart;
|
|
4784
|
-
const shouldRetitle = isStartPage && form.formType !== "other";
|
|
4785
|
-
return {
|
|
4786
|
-
...node,
|
|
4787
|
-
title: shouldRetitle ? titleFromFormHint(form, node.title) : node.title,
|
|
4788
|
-
metadata: {
|
|
4789
|
-
...node.metadata,
|
|
4790
|
-
formInventoryHint: {
|
|
4791
|
-
formType: form.formType,
|
|
4792
|
-
formNumber: form.formNumber,
|
|
4793
|
-
title: form.title,
|
|
4794
|
-
pageStart: form.pageStart,
|
|
4795
|
-
pageEnd: form.pageEnd
|
|
4796
|
-
}
|
|
4797
|
-
}
|
|
4798
|
-
};
|
|
4799
|
-
});
|
|
4800
|
-
const claimed = /* @__PURE__ */ new Set();
|
|
4801
|
-
for (const form of forms) {
|
|
4802
|
-
const config = formGroupConfig(form);
|
|
4803
|
-
const pageStart2 = form.pageStart;
|
|
4804
|
-
const pageEnd2 = form.pageEnd;
|
|
4805
|
-
if (!config || typeof pageStart2 !== "number" || typeof pageEnd2 !== "number") continue;
|
|
4806
|
-
const childIds = rootPages.filter(
|
|
4807
|
-
(page) => !claimed.has(page.id) && typeof page.pageStart === "number" && page.pageStart >= pageStart2 && page.pageStart <= pageEnd2
|
|
4808
|
-
).map((page) => page.id);
|
|
4809
|
-
if (childIds.length === 0) continue;
|
|
4810
|
-
nextTree = groupAdjacentChildren({
|
|
4811
|
-
sourceTree: nextTree,
|
|
4812
|
-
children,
|
|
4813
|
-
childIds,
|
|
4814
|
-
kind: config.kind,
|
|
4815
|
-
title: config.title,
|
|
4816
|
-
description: config.description,
|
|
4817
|
-
organizer: config.organizer
|
|
4818
|
-
});
|
|
4819
|
-
childIds.forEach((id) => claimed.add(id));
|
|
4820
|
-
}
|
|
4821
|
-
return normalizeDocumentSourceTreePaths(nextTree);
|
|
4822
|
-
}
|
|
4823
4611
|
function looksLikeDeclarationsStart(node) {
|
|
4824
4612
|
const title = cleanText(node.title, "");
|
|
4825
4613
|
const text = sourceNodeText(node);
|
|
@@ -4830,13 +4618,13 @@ function looksLikeDeclarationsStart(node) {
|
|
|
4830
4618
|
}
|
|
4831
4619
|
function looksLikeDeclarationsContinuation(node) {
|
|
4832
4620
|
const text = sourceNodeText(node);
|
|
4833
|
-
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);
|
|
4621
|
+
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);
|
|
4834
4622
|
}
|
|
4835
4623
|
function looksLikePolicyFormStart(node) {
|
|
4836
4624
|
const text = sourceNodeText(node);
|
|
4837
4625
|
const excerpt = cleanText(node.textExcerpt, "");
|
|
4838
4626
|
if (isAdministrativeNoticeNode(node) || looksLikeDeclarationsStart(node)) return false;
|
|
4839
|
-
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);
|
|
4627
|
+
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);
|
|
4840
4628
|
}
|
|
4841
4629
|
function looksLikePolicyFormContinuation(node) {
|
|
4842
4630
|
const text = sourceNodeText(node);
|
|
@@ -5598,25 +5386,51 @@ function isOperationalEvidenceAnchor(span) {
|
|
|
5598
5386
|
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;
|
|
5599
5387
|
return false;
|
|
5600
5388
|
}
|
|
5389
|
+
function operationalEvidencePages(sourceSpans) {
|
|
5390
|
+
const scores = /* @__PURE__ */ new Map();
|
|
5391
|
+
for (const span of sourceSpans) {
|
|
5392
|
+
const page = spanPageStart(span);
|
|
5393
|
+
if (typeof page !== "number") continue;
|
|
5394
|
+
const text = cleanText([span.text, span.formNumber, spanSourceUnit(span)].filter(Boolean).join(" "), "");
|
|
5395
|
+
if (!text) continue;
|
|
5396
|
+
let score = Math.max(0, operationalEvidenceScore(span));
|
|
5397
|
+
if (/\bdeclarations?\s+(page|schedule)?\b/i.test(text)) score += 24;
|
|
5398
|
+
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;
|
|
5399
|
+
if (/\bcoverage parts?,?\s+limits? of liability,?\s+deductibles?,?\s+and retroactive dates\b/i.test(text)) score += 24;
|
|
5400
|
+
if (/\b(policy\s*(number|period|term)|effective date|expiration date|expiry date|named insured|insurer|carrier|security)\b/i.test(text)) score += 12;
|
|
5401
|
+
if (/\b(premium|total due|tax|fee|producer|broker)\b/i.test(text)) score += 10;
|
|
5402
|
+
if (/\b(endorsement\s+(?:no\.?|number|#)?\s*[A-Z0-9]|attached at inception|forms? and endorsements?)\b/i.test(text)) score += 8;
|
|
5403
|
+
if (/\b(definitions?|exclusions?|conditions?|duties in the event|action against|cancellation by)\b/i.test(text)) score -= 12;
|
|
5404
|
+
if (score > 0) scores.set(page, (scores.get(page) ?? 0) + score);
|
|
5405
|
+
}
|
|
5406
|
+
return new Set(
|
|
5407
|
+
[...scores.entries()].filter(([, score]) => score >= 18).sort((left, right) => right[1] - left[1] || left[0] - right[0]).slice(0, 12).map(([page]) => page)
|
|
5408
|
+
);
|
|
5409
|
+
}
|
|
5601
5410
|
function operationalProfileEvidence(sourceTree, sourceSpans) {
|
|
5602
5411
|
const sorted = [...sourceSpans].sort(
|
|
5603
5412
|
(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)
|
|
5604
5413
|
);
|
|
5414
|
+
const selectedPages = operationalEvidencePages(sorted);
|
|
5605
5415
|
const selected = /* @__PURE__ */ new Set();
|
|
5606
5416
|
const selectedTableIds = /* @__PURE__ */ new Set();
|
|
5607
5417
|
for (let index = 0; index < sorted.length; index += 1) {
|
|
5608
|
-
const
|
|
5418
|
+
const span = sorted[index];
|
|
5419
|
+
const score = operationalEvidenceScore(span);
|
|
5609
5420
|
if (score < 8) continue;
|
|
5610
|
-
if (!isOperationalEvidenceAnchor(
|
|
5611
|
-
const
|
|
5612
|
-
if (
|
|
5613
|
-
const
|
|
5614
|
-
|
|
5421
|
+
if (!isOperationalEvidenceAnchor(span)) continue;
|
|
5422
|
+
const page = spanPageStart(span);
|
|
5423
|
+
if (selectedPages.size > 0 && typeof page === "number" && !selectedPages.has(page) && score < 30) continue;
|
|
5424
|
+
const tableId2 = spanTableId(span);
|
|
5425
|
+
if (tableId2 && !isTableCellSpan(span)) selectedTableIds.add(tableId2);
|
|
5426
|
+
const neighborWindow = score >= 24 ? 2 : 1;
|
|
5427
|
+
for (let offset = -neighborWindow; offset <= neighborWindow; offset += 1) {
|
|
5615
5428
|
const neighborIndex = index + offset;
|
|
5616
5429
|
const neighbor = sorted[neighborIndex];
|
|
5617
5430
|
if (!neighbor || spanPageStart(neighbor) !== page) continue;
|
|
5431
|
+
if (selectedPages.size > 0 && typeof page === "number" && !selectedPages.has(page) && operationalEvidenceScore(neighbor) < 30) continue;
|
|
5618
5432
|
const neighborText = cleanText(neighbor.text, "");
|
|
5619
|
-
if (!neighborText || neighborText.length >
|
|
5433
|
+
if (!neighborText || neighborText.length > 3e3) continue;
|
|
5620
5434
|
selected.add(neighborIndex);
|
|
5621
5435
|
}
|
|
5622
5436
|
}
|
|
@@ -5625,7 +5439,7 @@ function operationalProfileEvidence(sourceTree, sourceSpans) {
|
|
|
5625
5439
|
const tableId2 = spanTableId(span);
|
|
5626
5440
|
if (!tableId2 || !selectedTableIds.has(tableId2) || isTableCellSpan(span)) return;
|
|
5627
5441
|
const text = cleanText(span.text, "");
|
|
5628
|
-
if (text && text.length <=
|
|
5442
|
+
if (text && text.length <= 3e3) selected.add(index);
|
|
5629
5443
|
});
|
|
5630
5444
|
}
|
|
5631
5445
|
const nodeIdsBySpanId = sourceNodeIdsBySpanId(sourceTree);
|
|
@@ -5643,12 +5457,12 @@ function operationalProfileEvidence(sourceTree, sourceSpans) {
|
|
|
5643
5457
|
pageEnd: spanPageEnd(span),
|
|
5644
5458
|
sourceUnit: spanSourceUnit(span),
|
|
5645
5459
|
formNumber: span.formNumber,
|
|
5646
|
-
text: text.slice(0, span.sourceUnit === "page" ?
|
|
5460
|
+
text: text.slice(0, span.sourceUnit === "page" ? 900 : 700)
|
|
5647
5461
|
}];
|
|
5648
5462
|
});
|
|
5649
5463
|
const detailEntries = entries.filter((entry) => entry.sourceUnit !== "page");
|
|
5650
5464
|
const pageEntries = entries.filter((entry) => entry.sourceUnit === "page");
|
|
5651
|
-
return [...detailEntries, ...pageEntries
|
|
5465
|
+
return [...detailEntries.slice(0, 80), ...pageEntries.slice(0, 8)];
|
|
5652
5466
|
}
|
|
5653
5467
|
function sourceTreeRootId(sourceTree) {
|
|
5654
5468
|
return sourceTree.find((node) => node.kind === "document")?.id;
|
|
@@ -5691,13 +5505,15 @@ Rules:
|
|
|
5691
5505
|
- When citing an evidence entry, copy its sourceSpanId into the returned sourceSpanIds array.
|
|
5692
5506
|
- If a value is not directly supported, omit it.
|
|
5693
5507
|
- Prefer declarations, schedules, premium tables, and endorsement schedules over generic policy wording.
|
|
5508
|
+
- For effective, expiration, retroactive, and other date fields, return a normalized YYYY-MM-DD value when the source date is unambiguous, including month-name dates such as "20 Feb 2026". Do not emit fragmented date text such as "20 2 2026".
|
|
5694
5509
|
- For broker/producer, extract the agency or company legal name, not the license role, credential, or type. In a block like "Bayshore Insurance Brokers, LLC" followed by "Surplus Lines Broker - CA License No. ...", broker.value must be "Bayshore Insurance Brokers, LLC"; the surplus-lines role and license number are not the broker name.
|
|
5695
5510
|
- On declarations pages, treat "Item N" labels as section boundaries. Use Item 6 or equivalent coverage-schedule rows for coverage limits, deductibles, aggregate terms, and retroactive dates; do not merge Item 7 premium, Item 8 ERP, Item 9 producer, or Item 10 forms into Item 6 coverage facts.
|
|
5511
|
+
- Premium, tax, fee, payment-plan, rating, exposure, and reporting-value schedules are billing evidence, not coverage schedules. Extract the total policy premium into premium when supported, but do not create coverages[] entries from premium-only or fee-only rows, and never use Total Premium, MGA Fee, tax, stamping fee, reporting values, or exposure annual rate as a coverage limit.
|
|
5696
5512
|
- 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.
|
|
5697
5513
|
- 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.
|
|
5698
5514
|
- 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.
|
|
5699
5515
|
- 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.
|
|
5700
|
-
- Forms-and-endorsements schedules are
|
|
5516
|
+
- 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.
|
|
5701
5517
|
- 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.
|
|
5702
5518
|
- 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.
|
|
5703
5519
|
- 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.
|
|
@@ -5742,7 +5558,10 @@ var NORMALIZED_COMPATIBILITY_FIELDS = /* @__PURE__ */ new Set([
|
|
|
5742
5558
|
"policyNumber",
|
|
5743
5559
|
"namedInsured",
|
|
5744
5560
|
"insurer",
|
|
5745
|
-
"broker"
|
|
5561
|
+
"broker",
|
|
5562
|
+
"effectiveDate",
|
|
5563
|
+
"expirationDate",
|
|
5564
|
+
"retroactiveDate"
|
|
5746
5565
|
]);
|
|
5747
5566
|
function valueOf(profile, key) {
|
|
5748
5567
|
const value = profile[key];
|
|
@@ -5936,8 +5755,8 @@ async function cleanupOperationalCoverageSchedules(params) {
|
|
|
5936
5755
|
}
|
|
5937
5756
|
async function runSourceTreeExtraction(params) {
|
|
5938
5757
|
const sourceSpans = normalizeSourceSpans(params.sourceSpans);
|
|
5939
|
-
const formHints =
|
|
5940
|
-
let sourceTree = applySemanticPageGrouping(
|
|
5758
|
+
const formHints = [];
|
|
5759
|
+
let sourceTree = applySemanticPageGrouping(buildDocumentSourceTree(sourceSpans, params.id));
|
|
5941
5760
|
const warnings = [];
|
|
5942
5761
|
let modelCalls = 0;
|
|
5943
5762
|
let callsWithUsage = 0;
|