@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/README.md +6 -8
- package/dist/index.js +55 -311
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +55 -311
- 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
|
@@ -3952,16 +3952,6 @@ var OPERATIONAL_COVERAGE_TERM_KINDS2 = [
|
|
|
3952
3952
|
"other"
|
|
3953
3953
|
];
|
|
3954
3954
|
var OperationalCoverageTermKindSchema = import_zod21.z.enum(OPERATIONAL_COVERAGE_TERM_KINDS2);
|
|
3955
|
-
var CleanupTermSchema = import_zod21.z.object({
|
|
3956
|
-
kind: OperationalCoverageTermKindSchema,
|
|
3957
|
-
label: import_zod21.z.string(),
|
|
3958
|
-
value: import_zod21.z.string(),
|
|
3959
|
-
amount: import_zod21.z.number().nullable().optional(),
|
|
3960
|
-
appliesTo: import_zod21.z.string().nullable().optional(),
|
|
3961
|
-
sourceNodeIds: import_zod21.z.array(import_zod21.z.string()).optional(),
|
|
3962
|
-
sourceSpanIds: import_zod21.z.array(import_zod21.z.string()).optional(),
|
|
3963
|
-
reason: import_zod21.z.string().optional()
|
|
3964
|
-
});
|
|
3965
3955
|
var OperationalProfileCleanupSchema = import_zod21.z.object({
|
|
3966
3956
|
coverageDecisions: import_zod21.z.array(import_zod21.z.object({
|
|
3967
3957
|
coverageIndex: import_zod21.z.number().int().nonnegative(),
|
|
@@ -3985,8 +3975,7 @@ var OperationalProfileCleanupSchema = import_zod21.z.object({
|
|
|
3985
3975
|
appliesTo: import_zod21.z.string().nullable().optional(),
|
|
3986
3976
|
sourceNodeIds: import_zod21.z.array(import_zod21.z.string()).optional(),
|
|
3987
3977
|
sourceSpanIds: import_zod21.z.array(import_zod21.z.string()).optional()
|
|
3988
|
-
})).optional()
|
|
3989
|
-
termAdditions: import_zod21.z.array(CleanupTermSchema).optional()
|
|
3978
|
+
})).optional()
|
|
3990
3979
|
})).default([]),
|
|
3991
3980
|
warnings: import_zod21.z.array(import_zod21.z.string()).default([])
|
|
3992
3981
|
});
|
|
@@ -3994,7 +3983,6 @@ var CLEANUP_CANDIDATE_ID_LIMIT = 12;
|
|
|
3994
3983
|
var CLEANUP_SOURCE_NODE_LIMIT = 90;
|
|
3995
3984
|
var CLEANUP_SIBLING_WINDOW = 4;
|
|
3996
3985
|
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;
|
|
3997
|
-
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;
|
|
3998
3986
|
function compactNode(node, maxText = 700) {
|
|
3999
3987
|
return {
|
|
4000
3988
|
id: node.id,
|
|
@@ -4123,15 +4111,11 @@ function selectCoverageCleanupNodes(sourceTree, coverages) {
|
|
|
4123
4111
|
const children = childrenByParent.get(selectedNode.id) ?? [];
|
|
4124
4112
|
for (const child of children.slice(0, 24)) addNode(child, 760);
|
|
4125
4113
|
}
|
|
4126
|
-
const continuationPages = new Set([...coveragePages].map((page) => page + 1));
|
|
4127
4114
|
for (const node of sourceTree) {
|
|
4128
4115
|
if (node.kind === "document") continue;
|
|
4129
|
-
if (
|
|
4130
|
-
const samePage = coveragePages.has(node.pageStart);
|
|
4131
|
-
const nearbyContinuationPage = continuationPages.has(node.pageStart);
|
|
4132
|
-
if (!samePage && !nearbyContinuationPage) continue;
|
|
4116
|
+
if (!node.pageStart || !coveragePages.has(node.pageStart)) continue;
|
|
4133
4117
|
const text = nodeTextForSelection(node);
|
|
4134
|
-
if (CLEANUP_KEYWORD.test(text) || nodeTextMatchesCoverage(node, coverageTerms)
|
|
4118
|
+
if (CLEANUP_KEYWORD.test(text) || nodeTextMatchesCoverage(node, coverageTerms)) {
|
|
4135
4119
|
addNode(node, 600);
|
|
4136
4120
|
}
|
|
4137
4121
|
}
|
|
@@ -4145,7 +4129,7 @@ function buildOperationalProfileCleanupPrompt(sourceTree, profile, options = {})
|
|
|
4145
4129
|
const coverageEntries = coverageCleanupEntries(profile, options.coverageIndexes);
|
|
4146
4130
|
const nodes = selectCoverageCleanupNodes(sourceTree, coverageEntries.map((entry) => entry.coverage)).map((node) => compactNode(
|
|
4147
4131
|
node,
|
|
4148
|
-
node.kind === "page" || node.kind === "page_group" ?
|
|
4132
|
+
node.kind === "page" || node.kind === "page_group" ? 260 : node.kind === "table_row" || node.kind === "table_cell" ? 520 : 360
|
|
4149
4133
|
));
|
|
4150
4134
|
const candidate = {
|
|
4151
4135
|
documentType: profile.documentType,
|
|
@@ -4169,23 +4153,20 @@ Projection defects to look for:
|
|
|
4169
4153
|
- Item references such as "shown in Item 7" or bare item numbers treated as money amounts.
|
|
4170
4154
|
- Policy wording, exclusions, or unsupported prose copied into operational limit/deductible fields.
|
|
4171
4155
|
- Header/value splits where "Limit of Liability", "Deductible", "Retroactive Date", "Aggregate", "Each Claim", or similar terms are attached to the wrong coverage row.
|
|
4172
|
-
- 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.
|
|
4173
4156
|
- Repeated schedule headings projected as separate coverages when they only introduce the next coverage group.
|
|
4174
4157
|
|
|
4175
4158
|
Rules:
|
|
4176
4159
|
- Use internal reasoning, but return JSON decisions only.
|
|
4177
|
-
- Do not invent policy facts. Keep, drop, or update only existing coverageIndex and termIndex entries
|
|
4160
|
+
- Do not invent policy facts. Keep, drop, or update only existing coverageIndex and termIndex entries.
|
|
4178
4161
|
- Use sourceNodeIds and sourceSpanIds only from the provided source nodes or from the existing candidate entry.
|
|
4179
4162
|
- Prefer dropping a malformed fact over speculative rewriting.
|
|
4180
4163
|
- Keep a coverage when it is a real operational coverage/benefit even if only one term needs cleanup.
|
|
4181
4164
|
- 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.
|
|
4182
4165
|
- When changing a term's semantic meaning, set kind to the corrected normalized term kind.
|
|
4183
|
-
- Do not add new coverage rows.
|
|
4184
|
-
-
|
|
4185
|
-
- 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".
|
|
4186
|
-
- 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.
|
|
4166
|
+
- Do not add new coverage rows or new terms; this pass cleans the existing projection.
|
|
4167
|
+
- 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.
|
|
4187
4168
|
- 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.
|
|
4188
|
-
- For each coverage decision, always include termDecisions
|
|
4169
|
+
- For each coverage decision, always include termDecisions. Use [] when no terms need cleanup.
|
|
4189
4170
|
- Keep reasons concise and factual.
|
|
4190
4171
|
|
|
4191
4172
|
Candidate projection:
|
|
@@ -4326,50 +4307,9 @@ function applyTermCleanupDecision(term, decision, validNodeIds, validSpanIds) {
|
|
|
4326
4307
|
}
|
|
4327
4308
|
return next;
|
|
4328
4309
|
}
|
|
4329
|
-
function termAdditionTouches(addition, predicate) {
|
|
4330
|
-
return predicate({
|
|
4331
|
-
kind: addition.kind,
|
|
4332
|
-
label: cleanProfileValue(addition.label) ?? "",
|
|
4333
|
-
value: cleanProfileValue(addition.value) ?? ""
|
|
4334
|
-
});
|
|
4335
|
-
}
|
|
4336
4310
|
function termDecisionsTouch(coverage, decisions, predicate) {
|
|
4337
4311
|
return decisions.filter((decision) => decision.action !== "keep").some((decision) => termDecisionTouches(coverage, decision, predicate));
|
|
4338
4312
|
}
|
|
4339
|
-
function termAdditionsTouch(additions, predicate) {
|
|
4340
|
-
return additions.some((addition) => termAdditionTouches(addition, predicate));
|
|
4341
|
-
}
|
|
4342
|
-
function termKey(term) {
|
|
4343
|
-
return [
|
|
4344
|
-
term.kind,
|
|
4345
|
-
cleanProfileValue(term.label)?.toLowerCase(),
|
|
4346
|
-
cleanProfileValue(term.value)?.toLowerCase()
|
|
4347
|
-
].join("|");
|
|
4348
|
-
}
|
|
4349
|
-
function applyTermAddition(addition, validNodeIds, validSpanIds) {
|
|
4350
|
-
const label = cleanProfileValue(addition.label);
|
|
4351
|
-
const value = cleanProfileValue(addition.value);
|
|
4352
|
-
if (!label || !value) return void 0;
|
|
4353
|
-
const sourceNodeIds = validIds(addition.sourceNodeIds, validNodeIds);
|
|
4354
|
-
const sourceSpanIds = validIds(addition.sourceSpanIds, validSpanIds);
|
|
4355
|
-
if (sourceNodeIds.length === 0 && sourceSpanIds.length === 0) return void 0;
|
|
4356
|
-
const term = {
|
|
4357
|
-
kind: addition.kind,
|
|
4358
|
-
label,
|
|
4359
|
-
value,
|
|
4360
|
-
sourceNodeIds,
|
|
4361
|
-
sourceSpanIds
|
|
4362
|
-
};
|
|
4363
|
-
if (typeof addition.amount === "number" && Number.isFinite(addition.amount)) {
|
|
4364
|
-
term.amount = addition.amount;
|
|
4365
|
-
} else if (term.kind !== "retroactive_date") {
|
|
4366
|
-
const amount = amountFromOperationalValue(value);
|
|
4367
|
-
if (amount !== void 0) term.amount = amount;
|
|
4368
|
-
}
|
|
4369
|
-
const appliesTo = cleanProfileValue(addition.appliesTo);
|
|
4370
|
-
if (appliesTo) term.appliesTo = appliesTo;
|
|
4371
|
-
return term;
|
|
4372
|
-
}
|
|
4373
4313
|
function applyCoverageCleanupDecision(coverage, decision, validNodeIds, validSpanIds) {
|
|
4374
4314
|
if (!decision || decision.action === "keep") return coverage;
|
|
4375
4315
|
if (decision.action === "drop") return void 0;
|
|
@@ -4400,31 +4340,23 @@ function applyCoverageCleanupDecision(coverage, decision, validNodeIds, validSpa
|
|
|
4400
4340
|
const termDecisions = (decision.termDecisions ?? []).filter((termDecision) => termDecision.termIndex < coverage.limits.length);
|
|
4401
4341
|
const termDecisionByIndex = new Map(termDecisions.map((termDecision) => [termDecision.termIndex, termDecision]));
|
|
4402
4342
|
next.limits = coverage.limits.map((term, index) => applyTermCleanupDecision(term, termDecisionByIndex.get(index), validNodeIds, validSpanIds)).filter((term) => Boolean(term));
|
|
4403
|
-
|
|
4404
|
-
|
|
4405
|
-
for (const term of termAdditions) {
|
|
4406
|
-
const key = termKey(term);
|
|
4407
|
-
if (existingTermKeys.has(key)) continue;
|
|
4408
|
-
next.limits.push(term);
|
|
4409
|
-
existingTermKeys.add(key);
|
|
4410
|
-
}
|
|
4411
|
-
if (termDecisions.length > 0 || termAdditions.length > 0) {
|
|
4412
|
-
if (decision.limit == null && (termDecisionsTouch(coverage, termDecisions, isLimitTerm) || termAdditionsTouch(termAdditions, isLimitTerm))) {
|
|
4343
|
+
if (termDecisions.length > 0) {
|
|
4344
|
+
if (decision.limit == null && termDecisionsTouch(coverage, termDecisions, isLimitTerm)) {
|
|
4413
4345
|
const value = primaryLimitFromTerms(next.limits);
|
|
4414
4346
|
if (value) next.limit = value;
|
|
4415
4347
|
else delete next.limit;
|
|
4416
4348
|
}
|
|
4417
|
-
if (decision.deductible == null &&
|
|
4349
|
+
if (decision.deductible == null && termDecisionsTouch(coverage, termDecisions, isDeductibleTerm)) {
|
|
4418
4350
|
const value = deductibleFromTerms(next.limits);
|
|
4419
4351
|
if (value) next.deductible = value;
|
|
4420
4352
|
else delete next.deductible;
|
|
4421
4353
|
}
|
|
4422
|
-
if (decision.premium == null &&
|
|
4354
|
+
if (decision.premium == null && termDecisionsTouch(coverage, termDecisions, isPremiumTerm)) {
|
|
4423
4355
|
const value = premiumFromTerms(next.limits);
|
|
4424
4356
|
if (value) next.premium = value;
|
|
4425
4357
|
else delete next.premium;
|
|
4426
4358
|
}
|
|
4427
|
-
if (decision.retroactiveDate == null &&
|
|
4359
|
+
if (decision.retroactiveDate == null && termDecisionsTouch(coverage, termDecisions, isRetroactiveDateTerm)) {
|
|
4428
4360
|
const value = retroactiveDateFromTerms(next.limits);
|
|
4429
4361
|
if (value) next.retroactiveDate = value;
|
|
4430
4362
|
else delete next.retroactiveDate;
|
|
@@ -4651,36 +4583,6 @@ function spanPageEnd(span) {
|
|
|
4651
4583
|
function spanSourceUnit(span) {
|
|
4652
4584
|
return span.sourceUnit ?? span.metadata?.sourceUnit ?? span.metadata?.elementType;
|
|
4653
4585
|
}
|
|
4654
|
-
function formNumberFromText(value) {
|
|
4655
|
-
return cleanText(value, "").match(/\b[A-Z]{2,}(?:-[A-Z0-9]+)+\s+\d{2}\s+\d{2}\b/)?.[0]?.replace(/\s+/g, " ");
|
|
4656
|
-
}
|
|
4657
|
-
function editionDateFromFormNumber(formNumber) {
|
|
4658
|
-
const match = formNumber?.match(/\b(\d{2})\s+(\d{2})$/);
|
|
4659
|
-
return match ? `${match[1]}/${match[2]}` : void 0;
|
|
4660
|
-
}
|
|
4661
|
-
function pageTitleFromText(text, fallback) {
|
|
4662
|
-
const normalized = cleanText(text, fallback);
|
|
4663
|
-
const patterns = [
|
|
4664
|
-
/\bIMPORTANT NOTICE\s+[—-]\s+HOW TO REPORT A CLAIM\b/i,
|
|
4665
|
-
/\bPRIVACY NOTICE TO POLICYHOLDERS\b/i,
|
|
4666
|
-
/\bOFAC ADVISORY NOTICE\b/i,
|
|
4667
|
-
/\bTERRORISM RISK INSURANCE ACT\s*\(TRIA\)\s*DISCLOSURE AND REJECTION\b/i,
|
|
4668
|
-
/\bDECLARATIONS PAGE\b/i,
|
|
4669
|
-
/\bTECHNOLOGY ERRORS?\s*&\s*OMISSIONS AND CYBER LIABILITY INSURANCE POLICY\b/i,
|
|
4670
|
-
/\bTRADE OR ECONOMIC SANCTIONS LIMITATION\b/i,
|
|
4671
|
-
/\bFORMS? AND ENDORSEMENTS\b/i
|
|
4672
|
-
];
|
|
4673
|
-
for (const pattern of patterns) {
|
|
4674
|
-
const match = normalized.match(pattern)?.[0];
|
|
4675
|
-
if (match) return cleanText(match, fallback);
|
|
4676
|
-
}
|
|
4677
|
-
const endorsement = normalized.match(/\bENDORSEMENT\s+(?:NO\.?|NUMBER|#)\s*[A-Z0-9][A-Z0-9.-]*\b/i)?.[0];
|
|
4678
|
-
if (endorsement) return cleanText(endorsement, fallback);
|
|
4679
|
-
const firstSentence = normalized.split(/(?<=\.)\s+/)[0];
|
|
4680
|
-
if (/^page\s+\d+\b/i.test(firstSentence)) return fallback;
|
|
4681
|
-
if (firstSentence && firstSentence.length <= 120) return firstSentence.replace(/[.]$/, "");
|
|
4682
|
-
return fallback;
|
|
4683
|
-
}
|
|
4684
4586
|
function pageHeadingTitleFromText(text, fallback) {
|
|
4685
4587
|
const normalized = cleanText(text, "");
|
|
4686
4588
|
const headingText = normalized.replace(/^page\s+\d+\s*(?:\|\s*page\s*\|\s*page\s+\d+\s*\|?)?/i, "").slice(0, 700);
|
|
@@ -4700,194 +4602,9 @@ function pageHeadingTitleFromText(text, fallback) {
|
|
|
4700
4602
|
}
|
|
4701
4603
|
return fallback;
|
|
4702
4604
|
}
|
|
4703
|
-
function pageFormTypeFromText(text) {
|
|
4704
|
-
if (hasSubstantiveDeclarationsScheduleText(text)) return "declarations";
|
|
4705
|
-
if (/\b(declarations?\s+page|declarations?\s+schedule)\b/i.test(text)) return "declarations";
|
|
4706
|
-
if (/\b(endorsement\s+(?:no\.?|number|#)|this endorsement changes the policy|[A-Z]{2,}-END\s+\d{2,})\b/i.test(text)) return "endorsement";
|
|
4707
|
-
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";
|
|
4708
|
-
if (/\b(important notice|privacy notice|ofac advisory|terrorism risk insurance act|tria|trade or economic sanctions)\b/i.test(text)) return "notice";
|
|
4709
|
-
return "other";
|
|
4710
|
-
}
|
|
4711
4605
|
function hasSubstantiveDeclarationsScheduleText(text) {
|
|
4712
4606
|
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);
|
|
4713
4607
|
}
|
|
4714
|
-
function administrativeFormTypeFromText(text) {
|
|
4715
|
-
if (hasSubstantiveDeclarationsScheduleText(text)) return void 0;
|
|
4716
|
-
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)) {
|
|
4717
|
-
return "notice";
|
|
4718
|
-
}
|
|
4719
|
-
if (/\b(specimen policy|policy jacket|countersigned|countersignature|licensed resident agent|corporate secretary|president and ceo|application of insurance executed)\b/i.test(text)) {
|
|
4720
|
-
return "other";
|
|
4721
|
-
}
|
|
4722
|
-
return void 0;
|
|
4723
|
-
}
|
|
4724
|
-
function pageTextByNumber(sourceSpans) {
|
|
4725
|
-
const pageTexts = /* @__PURE__ */ new Map();
|
|
4726
|
-
const pageSpanTexts = /* @__PURE__ */ new Map();
|
|
4727
|
-
for (const span of sourceSpans) {
|
|
4728
|
-
const start = spanPageStart(span);
|
|
4729
|
-
if (typeof start !== "number") continue;
|
|
4730
|
-
const end = spanPageEnd(span) ?? start;
|
|
4731
|
-
for (let page = start; page <= end; page += 1) {
|
|
4732
|
-
if (spanSourceUnit(span) === "page") {
|
|
4733
|
-
pageSpanTexts.set(page, cleanText(span.text, ""));
|
|
4734
|
-
continue;
|
|
4735
|
-
}
|
|
4736
|
-
const existing = pageTexts.get(page) ?? "";
|
|
4737
|
-
if (existing.length < 4e3) pageTexts.set(page, cleanText([existing, span.text].filter(Boolean).join(" "), ""));
|
|
4738
|
-
}
|
|
4739
|
-
}
|
|
4740
|
-
return new Map([.../* @__PURE__ */ new Set([...pageTexts.keys(), ...pageSpanTexts.keys()])].map((page) => [
|
|
4741
|
-
page,
|
|
4742
|
-
pageSpanTexts.get(page) ?? pageTexts.get(page) ?? ""
|
|
4743
|
-
]));
|
|
4744
|
-
}
|
|
4745
|
-
function reconcileFormTypeWithSourceText(form, pageTexts) {
|
|
4746
|
-
const pages = [];
|
|
4747
|
-
const start = form.pageStart;
|
|
4748
|
-
const end = form.pageEnd ?? start;
|
|
4749
|
-
if (typeof start === "number" && typeof end === "number") {
|
|
4750
|
-
for (let page = start; page <= end; page += 1) pages.push(pageTexts.get(page) ?? "");
|
|
4751
|
-
}
|
|
4752
|
-
const text = cleanText([form.title, form.formNumber, ...pages].filter(Boolean).join(" "), "");
|
|
4753
|
-
const administrativeType = administrativeFormTypeFromText(text);
|
|
4754
|
-
if (administrativeType && form.formType !== administrativeType) return administrativeType;
|
|
4755
|
-
return form.formType;
|
|
4756
|
-
}
|
|
4757
|
-
function inferFormHintsFromSourceSpans(sourceSpans) {
|
|
4758
|
-
const pageTexts = pageTextByNumber(sourceSpans);
|
|
4759
|
-
if (pageTexts.size === 0) return [];
|
|
4760
|
-
const pageHints = [...pageTexts.keys()].sort((left, right) => left - right).map((page) => {
|
|
4761
|
-
const text = pageTexts.get(page) ?? "";
|
|
4762
|
-
const formNumber = formNumberFromText(text);
|
|
4763
|
-
return {
|
|
4764
|
-
formNumber,
|
|
4765
|
-
editionDate: editionDateFromFormNumber(formNumber),
|
|
4766
|
-
title: pageTitleFromText(text, `Page ${page}`),
|
|
4767
|
-
formType: pageFormTypeFromText(text),
|
|
4768
|
-
pageStart: page,
|
|
4769
|
-
pageEnd: page
|
|
4770
|
-
};
|
|
4771
|
-
});
|
|
4772
|
-
const merged = [];
|
|
4773
|
-
for (const hint of pageHints) {
|
|
4774
|
-
const previous = merged[merged.length - 1];
|
|
4775
|
-
const startsNewEndorsement = hint.formType === "endorsement" && /\bendorsement\s+(?:no\.?|number|#)\s*[A-Z0-9]|this endorsement changes the policy/i.test(hint.title ?? "");
|
|
4776
|
-
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);
|
|
4777
|
-
if (!canMerge) {
|
|
4778
|
-
merged.push(hint);
|
|
4779
|
-
continue;
|
|
4780
|
-
}
|
|
4781
|
-
previous.pageEnd = hint.pageEnd;
|
|
4782
|
-
previous.title = previous.title ?? hint.title;
|
|
4783
|
-
previous.formNumber = previous.formNumber ?? hint.formNumber;
|
|
4784
|
-
previous.editionDate = previous.editionDate ?? hint.editionDate;
|
|
4785
|
-
}
|
|
4786
|
-
return merged;
|
|
4787
|
-
}
|
|
4788
|
-
function normalizeFormHints(forms, sourceSpans) {
|
|
4789
|
-
const pageTexts = pageTextByNumber(sourceSpans);
|
|
4790
|
-
const provided = (forms ?? []).filter(
|
|
4791
|
-
(form) => typeof form.pageStart === "number" && typeof form.pageEnd === "number" && form.pageStart > 0 && form.pageEnd >= form.pageStart
|
|
4792
|
-
).map((form) => ({
|
|
4793
|
-
...form,
|
|
4794
|
-
formType: reconcileFormTypeWithSourceText(form, pageTexts),
|
|
4795
|
-
title: form.title ? cleanText(form.title, "") : void 0
|
|
4796
|
-
})).sort(
|
|
4797
|
-
(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)
|
|
4798
|
-
);
|
|
4799
|
-
return provided.length ? provided : inferFormHintsFromSourceSpans(sourceSpans);
|
|
4800
|
-
}
|
|
4801
|
-
function formHintForPage(forms, page) {
|
|
4802
|
-
return forms.find(
|
|
4803
|
-
(form) => typeof form.pageStart === "number" && typeof form.pageEnd === "number" && page >= form.pageStart && page <= form.pageEnd
|
|
4804
|
-
);
|
|
4805
|
-
}
|
|
4806
|
-
function titleFromFormHint(form, fallback) {
|
|
4807
|
-
if (form.formType === "declarations") return "Declarations";
|
|
4808
|
-
if (form.formType === "coverage") return "Policy Form";
|
|
4809
|
-
if (form.formType === "endorsement") return endorsementTitle([form.title, form.formNumber].filter(Boolean).join(" ")) ?? cleanText(form.title, fallback);
|
|
4810
|
-
return cleanText(form.title, fallback);
|
|
4811
|
-
}
|
|
4812
|
-
function formGroupConfig(form) {
|
|
4813
|
-
if (form.formType === "declarations") {
|
|
4814
|
-
return {
|
|
4815
|
-
kind: "page_group",
|
|
4816
|
-
title: "Declarations",
|
|
4817
|
-
description: "Declarations pages and schedules grouped from form inventory",
|
|
4818
|
-
organizer: "form_inventory_declarations_grouping"
|
|
4819
|
-
};
|
|
4820
|
-
}
|
|
4821
|
-
if (form.formType === "coverage") {
|
|
4822
|
-
return {
|
|
4823
|
-
kind: "form",
|
|
4824
|
-
title: "Policy Form",
|
|
4825
|
-
description: "Policy form pages grouped from form inventory",
|
|
4826
|
-
organizer: "form_inventory_policy_form_grouping"
|
|
4827
|
-
};
|
|
4828
|
-
}
|
|
4829
|
-
if (form.formType === "endorsement") {
|
|
4830
|
-
const title = titleFromFormHint(form, "Endorsement");
|
|
4831
|
-
return {
|
|
4832
|
-
kind: "endorsement",
|
|
4833
|
-
title,
|
|
4834
|
-
description: `${title} grouped from form inventory`,
|
|
4835
|
-
organizer: "form_inventory_endorsement_grouping"
|
|
4836
|
-
};
|
|
4837
|
-
}
|
|
4838
|
-
return void 0;
|
|
4839
|
-
}
|
|
4840
|
-
function applyFormInventoryHints(sourceTree, forms) {
|
|
4841
|
-
if (forms.length === 0) return sourceTree;
|
|
4842
|
-
const rootId = sourceTreeRootId(sourceTree);
|
|
4843
|
-
if (!rootId) return sourceTree;
|
|
4844
|
-
const byParent = nodesByParent(sourceTree);
|
|
4845
|
-
const children = (byParent.get(rootId) ?? []).filter((node) => node.kind !== "document").sort((left, right) => left.order - right.order);
|
|
4846
|
-
const rootPages = children.filter((node) => node.kind === "page" && typeof node.pageStart === "number");
|
|
4847
|
-
let nextTree = sourceTree.map((node) => {
|
|
4848
|
-
if (node.kind !== "page" || typeof node.pageStart !== "number") return node;
|
|
4849
|
-
const form = formHintForPage(forms, node.pageStart);
|
|
4850
|
-
if (!form) return node;
|
|
4851
|
-
const isStartPage = form.pageStart === node.pageStart;
|
|
4852
|
-
const shouldRetitle = isStartPage && form.formType !== "other";
|
|
4853
|
-
return {
|
|
4854
|
-
...node,
|
|
4855
|
-
title: shouldRetitle ? titleFromFormHint(form, node.title) : node.title,
|
|
4856
|
-
metadata: {
|
|
4857
|
-
...node.metadata,
|
|
4858
|
-
formInventoryHint: {
|
|
4859
|
-
formType: form.formType,
|
|
4860
|
-
formNumber: form.formNumber,
|
|
4861
|
-
title: form.title,
|
|
4862
|
-
pageStart: form.pageStart,
|
|
4863
|
-
pageEnd: form.pageEnd
|
|
4864
|
-
}
|
|
4865
|
-
}
|
|
4866
|
-
};
|
|
4867
|
-
});
|
|
4868
|
-
const claimed = /* @__PURE__ */ new Set();
|
|
4869
|
-
for (const form of forms) {
|
|
4870
|
-
const config = formGroupConfig(form);
|
|
4871
|
-
const pageStart2 = form.pageStart;
|
|
4872
|
-
const pageEnd2 = form.pageEnd;
|
|
4873
|
-
if (!config || typeof pageStart2 !== "number" || typeof pageEnd2 !== "number") continue;
|
|
4874
|
-
const childIds = rootPages.filter(
|
|
4875
|
-
(page) => !claimed.has(page.id) && typeof page.pageStart === "number" && page.pageStart >= pageStart2 && page.pageStart <= pageEnd2
|
|
4876
|
-
).map((page) => page.id);
|
|
4877
|
-
if (childIds.length === 0) continue;
|
|
4878
|
-
nextTree = groupAdjacentChildren({
|
|
4879
|
-
sourceTree: nextTree,
|
|
4880
|
-
children,
|
|
4881
|
-
childIds,
|
|
4882
|
-
kind: config.kind,
|
|
4883
|
-
title: config.title,
|
|
4884
|
-
description: config.description,
|
|
4885
|
-
organizer: config.organizer
|
|
4886
|
-
});
|
|
4887
|
-
childIds.forEach((id) => claimed.add(id));
|
|
4888
|
-
}
|
|
4889
|
-
return normalizeDocumentSourceTreePaths(nextTree);
|
|
4890
|
-
}
|
|
4891
4608
|
function looksLikeDeclarationsStart(node) {
|
|
4892
4609
|
const title = cleanText(node.title, "");
|
|
4893
4610
|
const text = sourceNodeText(node);
|
|
@@ -4898,13 +4615,13 @@ function looksLikeDeclarationsStart(node) {
|
|
|
4898
4615
|
}
|
|
4899
4616
|
function looksLikeDeclarationsContinuation(node) {
|
|
4900
4617
|
const text = sourceNodeText(node);
|
|
4901
|
-
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);
|
|
4618
|
+
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);
|
|
4902
4619
|
}
|
|
4903
4620
|
function looksLikePolicyFormStart(node) {
|
|
4904
4621
|
const text = sourceNodeText(node);
|
|
4905
4622
|
const excerpt = cleanText(node.textExcerpt, "");
|
|
4906
4623
|
if (isAdministrativeNoticeNode(node) || looksLikeDeclarationsStart(node)) return false;
|
|
4907
|
-
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);
|
|
4624
|
+
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);
|
|
4908
4625
|
}
|
|
4909
4626
|
function looksLikePolicyFormContinuation(node) {
|
|
4910
4627
|
const text = sourceNodeText(node);
|
|
@@ -5666,25 +5383,51 @@ function isOperationalEvidenceAnchor(span) {
|
|
|
5666
5383
|
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;
|
|
5667
5384
|
return false;
|
|
5668
5385
|
}
|
|
5386
|
+
function operationalEvidencePages(sourceSpans) {
|
|
5387
|
+
const scores = /* @__PURE__ */ new Map();
|
|
5388
|
+
for (const span of sourceSpans) {
|
|
5389
|
+
const page = spanPageStart(span);
|
|
5390
|
+
if (typeof page !== "number") continue;
|
|
5391
|
+
const text = cleanText([span.text, span.formNumber, spanSourceUnit(span)].filter(Boolean).join(" "), "");
|
|
5392
|
+
if (!text) continue;
|
|
5393
|
+
let score = Math.max(0, operationalEvidenceScore(span));
|
|
5394
|
+
if (/\bdeclarations?\s+(page|schedule)?\b/i.test(text)) score += 24;
|
|
5395
|
+
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;
|
|
5396
|
+
if (/\bcoverage parts?,?\s+limits? of liability,?\s+deductibles?,?\s+and retroactive dates\b/i.test(text)) score += 24;
|
|
5397
|
+
if (/\b(policy\s*(number|period|term)|effective date|expiration date|expiry date|named insured|insurer|carrier|security)\b/i.test(text)) score += 12;
|
|
5398
|
+
if (/\b(premium|total due|tax|fee|producer|broker)\b/i.test(text)) score += 10;
|
|
5399
|
+
if (/\b(endorsement\s+(?:no\.?|number|#)?\s*[A-Z0-9]|attached at inception|forms? and endorsements?)\b/i.test(text)) score += 8;
|
|
5400
|
+
if (/\b(definitions?|exclusions?|conditions?|duties in the event|action against|cancellation by)\b/i.test(text)) score -= 12;
|
|
5401
|
+
if (score > 0) scores.set(page, (scores.get(page) ?? 0) + score);
|
|
5402
|
+
}
|
|
5403
|
+
return new Set(
|
|
5404
|
+
[...scores.entries()].filter(([, score]) => score >= 18).sort((left, right) => right[1] - left[1] || left[0] - right[0]).slice(0, 12).map(([page]) => page)
|
|
5405
|
+
);
|
|
5406
|
+
}
|
|
5669
5407
|
function operationalProfileEvidence(sourceTree, sourceSpans) {
|
|
5670
5408
|
const sorted = [...sourceSpans].sort(
|
|
5671
5409
|
(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)
|
|
5672
5410
|
);
|
|
5411
|
+
const selectedPages = operationalEvidencePages(sorted);
|
|
5673
5412
|
const selected = /* @__PURE__ */ new Set();
|
|
5674
5413
|
const selectedTableIds = /* @__PURE__ */ new Set();
|
|
5675
5414
|
for (let index = 0; index < sorted.length; index += 1) {
|
|
5676
|
-
const
|
|
5415
|
+
const span = sorted[index];
|
|
5416
|
+
const score = operationalEvidenceScore(span);
|
|
5677
5417
|
if (score < 8) continue;
|
|
5678
|
-
if (!isOperationalEvidenceAnchor(
|
|
5679
|
-
const
|
|
5680
|
-
if (
|
|
5681
|
-
const
|
|
5682
|
-
|
|
5418
|
+
if (!isOperationalEvidenceAnchor(span)) continue;
|
|
5419
|
+
const page = spanPageStart(span);
|
|
5420
|
+
if (selectedPages.size > 0 && typeof page === "number" && !selectedPages.has(page) && score < 30) continue;
|
|
5421
|
+
const tableId2 = spanTableId(span);
|
|
5422
|
+
if (tableId2 && !isTableCellSpan(span)) selectedTableIds.add(tableId2);
|
|
5423
|
+
const neighborWindow = score >= 24 ? 2 : 1;
|
|
5424
|
+
for (let offset = -neighborWindow; offset <= neighborWindow; offset += 1) {
|
|
5683
5425
|
const neighborIndex = index + offset;
|
|
5684
5426
|
const neighbor = sorted[neighborIndex];
|
|
5685
5427
|
if (!neighbor || spanPageStart(neighbor) !== page) continue;
|
|
5428
|
+
if (selectedPages.size > 0 && typeof page === "number" && !selectedPages.has(page) && operationalEvidenceScore(neighbor) < 30) continue;
|
|
5686
5429
|
const neighborText = cleanText(neighbor.text, "");
|
|
5687
|
-
if (!neighborText || neighborText.length >
|
|
5430
|
+
if (!neighborText || neighborText.length > 3e3) continue;
|
|
5688
5431
|
selected.add(neighborIndex);
|
|
5689
5432
|
}
|
|
5690
5433
|
}
|
|
@@ -5693,7 +5436,7 @@ function operationalProfileEvidence(sourceTree, sourceSpans) {
|
|
|
5693
5436
|
const tableId2 = spanTableId(span);
|
|
5694
5437
|
if (!tableId2 || !selectedTableIds.has(tableId2) || isTableCellSpan(span)) return;
|
|
5695
5438
|
const text = cleanText(span.text, "");
|
|
5696
|
-
if (text && text.length <=
|
|
5439
|
+
if (text && text.length <= 3e3) selected.add(index);
|
|
5697
5440
|
});
|
|
5698
5441
|
}
|
|
5699
5442
|
const nodeIdsBySpanId = sourceNodeIdsBySpanId(sourceTree);
|
|
@@ -5711,12 +5454,12 @@ function operationalProfileEvidence(sourceTree, sourceSpans) {
|
|
|
5711
5454
|
pageEnd: spanPageEnd(span),
|
|
5712
5455
|
sourceUnit: spanSourceUnit(span),
|
|
5713
5456
|
formNumber: span.formNumber,
|
|
5714
|
-
text: text.slice(0, span.sourceUnit === "page" ?
|
|
5457
|
+
text: text.slice(0, span.sourceUnit === "page" ? 900 : 700)
|
|
5715
5458
|
}];
|
|
5716
5459
|
});
|
|
5717
5460
|
const detailEntries = entries.filter((entry) => entry.sourceUnit !== "page");
|
|
5718
5461
|
const pageEntries = entries.filter((entry) => entry.sourceUnit === "page");
|
|
5719
|
-
return [...detailEntries, ...pageEntries
|
|
5462
|
+
return [...detailEntries.slice(0, 80), ...pageEntries.slice(0, 8)];
|
|
5720
5463
|
}
|
|
5721
5464
|
function sourceTreeRootId(sourceTree) {
|
|
5722
5465
|
return sourceTree.find((node) => node.kind === "document")?.id;
|
|
@@ -5764,7 +5507,8 @@ Rules:
|
|
|
5764
5507
|
- 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.
|
|
5765
5508
|
- 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.
|
|
5766
5509
|
- 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.
|
|
5767
|
-
-
|
|
5510
|
+
- 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.
|
|
5511
|
+
- 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.
|
|
5768
5512
|
- 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.
|
|
5769
5513
|
- 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.
|
|
5770
5514
|
- 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.
|
|
@@ -6003,8 +5747,8 @@ async function cleanupOperationalCoverageSchedules(params) {
|
|
|
6003
5747
|
}
|
|
6004
5748
|
async function runSourceTreeExtraction(params) {
|
|
6005
5749
|
const sourceSpans = normalizeSourceSpans(params.sourceSpans);
|
|
6006
|
-
const formHints =
|
|
6007
|
-
let sourceTree = applySemanticPageGrouping(
|
|
5750
|
+
const formHints = [];
|
|
5751
|
+
let sourceTree = applySemanticPageGrouping(buildDocumentSourceTree(sourceSpans, params.id));
|
|
6008
5752
|
const warnings = [];
|
|
6009
5753
|
let modelCalls = 0;
|
|
6010
5754
|
let callsWithUsage = 0;
|