@claritylabs/cl-sdk 3.0.5 → 3.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +270 -3
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +270 -3
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -9069,6 +9069,7 @@ function simplifyOrganizerTitle(value, fallback, kind) {
|
|
|
9069
9069
|
if (/^declarations\b/i.test(title)) return "Declarations";
|
|
9070
9070
|
if (/^policy\s+form\b/i.test(title)) return "Policy Form";
|
|
9071
9071
|
if (/^definitions\b/i.test(title)) return "Definitions";
|
|
9072
|
+
if (kind === "page_group" && /^endorsements?\b/i.test(title)) return "Endorsements";
|
|
9072
9073
|
const endorsementNumber = title.match(/^endorsement\s+(?:no\.?|number|#)?\s*([A-Z0-9][A-Z0-9.-]*)\b/i)?.[1];
|
|
9073
9074
|
if (endorsementNumber) return `Endorsement No. ${endorsementNumber}`;
|
|
9074
9075
|
if (kind === "endorsement" && /^endorsements?\s+\d+\s*[–-]\s*\d+\b/i.test(title)) {
|
|
@@ -9077,9 +9078,159 @@ function simplifyOrganizerTitle(value, fallback, kind) {
|
|
|
9077
9078
|
return title;
|
|
9078
9079
|
}
|
|
9079
9080
|
function endorsementReference(value) {
|
|
9080
|
-
|
|
9081
|
+
const text = cleanText(value, "");
|
|
9082
|
+
const explicit = text.match(/\bendorsement\s+(?:no\.?|number|#)?\s*([A-Z0-9][A-Z0-9.-]*)\b/i)?.[1]?.toUpperCase();
|
|
9083
|
+
if (explicit) return explicit;
|
|
9084
|
+
return text.match(/\b(?:[A-Z]{2,}-)?END\s+0*([0-9]{1,4})\b/i)?.[1]?.toUpperCase();
|
|
9085
|
+
}
|
|
9086
|
+
function endorsementTitle(value) {
|
|
9087
|
+
const text = cleanText(value, "");
|
|
9088
|
+
const explicit = text.match(/\bendorsement\s+(?:no\.?|number|#)\s*([A-Z0-9][A-Z0-9.-]*)\b/i)?.[1]?.toUpperCase();
|
|
9089
|
+
const number = explicit ?? text.match(/\b(?:[A-Z]{2,}-)?END\s+0*([0-9]{1,4})\b/i)?.[1]?.toUpperCase();
|
|
9090
|
+
return number ? `Endorsement No. ${number}` : void 0;
|
|
9091
|
+
}
|
|
9092
|
+
function sourceNodeText(node) {
|
|
9093
|
+
return cleanText([node.title, node.description, node.textExcerpt].filter(Boolean).join(" "), "");
|
|
9094
|
+
}
|
|
9095
|
+
function semanticGroupNodeId(documentId, kind, title, childNodeIds) {
|
|
9096
|
+
return [
|
|
9097
|
+
documentId.replace(/[^a-zA-Z0-9_.:-]/g, "_"),
|
|
9098
|
+
"source_node",
|
|
9099
|
+
kind,
|
|
9100
|
+
title.replace(/[^a-zA-Z0-9_.:-]/g, "_").toLowerCase().slice(0, 48),
|
|
9101
|
+
childNodeIds.join("_").replace(/[^a-zA-Z0-9_.:-]/g, "_").slice(0, 80)
|
|
9102
|
+
].join(":");
|
|
9103
|
+
}
|
|
9104
|
+
function looksLikeDeclarationsStart(node) {
|
|
9105
|
+
return /\bdeclarations?\s+(page|schedule)\b/i.test(sourceNodeText(node));
|
|
9106
|
+
}
|
|
9107
|
+
function looksLikeDeclarationsContinuation(node) {
|
|
9108
|
+
const text = sourceNodeText(node);
|
|
9109
|
+
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);
|
|
9110
|
+
}
|
|
9111
|
+
function looksLikePolicyFormStart(node) {
|
|
9112
|
+
const text = sourceNodeText(node);
|
|
9113
|
+
return /\bpolicy form\b/i.test(node.title) || /\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);
|
|
9114
|
+
}
|
|
9115
|
+
function looksLikePolicyFormContinuation(node) {
|
|
9116
|
+
const text = sourceNodeText(node);
|
|
9117
|
+
if (looksLikePolicyFormStart(node)) return true;
|
|
9118
|
+
return /\b(insuring agreement|definitions?|exclusions?|conditions?|claim means|insured means|wrongful act means|limits of liability|notice of claim|cancellation by|action against the company)\b/i.test(text);
|
|
9119
|
+
}
|
|
9120
|
+
function groupAdjacentChildren(params) {
|
|
9121
|
+
if (params.childIds.length < 2) return params.sourceTree;
|
|
9122
|
+
const children = params.childIds.map((id2) => params.children.find((child) => child.id === id2)).filter((child) => Boolean(child));
|
|
9123
|
+
if (children.length < 2) return params.sourceTree;
|
|
9124
|
+
const parentId = children[0].parentId;
|
|
9125
|
+
if (!children.every((child) => child.parentId === parentId)) return params.sourceTree;
|
|
9126
|
+
const documentId = children[0].documentId;
|
|
9127
|
+
const id = semanticGroupNodeId(documentId, params.kind, params.title, children.map((child) => child.id));
|
|
9128
|
+
if (params.sourceTree.some((node) => node.id === id)) return params.sourceTree;
|
|
9129
|
+
const pageStarts = children.map((child) => child.pageStart).filter((page) => typeof page === "number");
|
|
9130
|
+
const pageEnds = children.map((child) => child.pageEnd ?? child.pageStart).filter((page) => typeof page === "number");
|
|
9131
|
+
const sourceSpanIds = [...new Set(children.flatMap((child) => child.sourceSpanIds))];
|
|
9132
|
+
const order = Math.min(...children.map((child) => child.order));
|
|
9133
|
+
const groupNode = {
|
|
9134
|
+
id,
|
|
9135
|
+
documentId,
|
|
9136
|
+
parentId,
|
|
9137
|
+
kind: params.kind,
|
|
9138
|
+
title: params.title,
|
|
9139
|
+
description: params.description,
|
|
9140
|
+
textExcerpt: children.map((child) => child.textExcerpt ?? child.description).filter(Boolean).join("\n\n").slice(0, 1600),
|
|
9141
|
+
sourceSpanIds,
|
|
9142
|
+
pageStart: pageStarts.length ? Math.min(...pageStarts) : void 0,
|
|
9143
|
+
pageEnd: pageEnds.length ? Math.max(...pageEnds) : void 0,
|
|
9144
|
+
bbox: children.flatMap((child) => child.bbox ?? []).slice(0, 12),
|
|
9145
|
+
order,
|
|
9146
|
+
path: "",
|
|
9147
|
+
metadata: { sourceTreeVersion: "v3", organizer: params.organizer }
|
|
9148
|
+
};
|
|
9149
|
+
const wanted = new Set(children.map((child) => child.id));
|
|
9150
|
+
return [
|
|
9151
|
+
...params.sourceTree.map(
|
|
9152
|
+
(node) => wanted.has(node.id) ? { ...node, parentId: id, order: node.order + 1e-3 } : node
|
|
9153
|
+
),
|
|
9154
|
+
groupNode
|
|
9155
|
+
];
|
|
9156
|
+
}
|
|
9157
|
+
function applySemanticPageGrouping(sourceTree) {
|
|
9158
|
+
const relabeled = sourceTree.map((node) => {
|
|
9159
|
+
if (node.kind === "document" || node.kind === "page_group") return node;
|
|
9160
|
+
const text = sourceNodeText(node);
|
|
9161
|
+
const endorsement = endorsementTitle(text);
|
|
9162
|
+
if (endorsement && node.kind === "page") {
|
|
9163
|
+
return {
|
|
9164
|
+
...node,
|
|
9165
|
+
kind: "endorsement",
|
|
9166
|
+
title: endorsement,
|
|
9167
|
+
description: cleanText([endorsement, "endorsement", node.pageStart ? `page ${node.pageStart}` : void 0, node.textExcerpt].filter(Boolean).join(" | "), endorsement),
|
|
9168
|
+
metadata: { ...node.metadata, organizerRepair: "semantic_page_grouping" }
|
|
9169
|
+
};
|
|
9170
|
+
}
|
|
9171
|
+
if (node.kind === "page" && looksLikeDeclarationsStart(node)) {
|
|
9172
|
+
return {
|
|
9173
|
+
...node,
|
|
9174
|
+
title: "Declarations",
|
|
9175
|
+
description: cleanText([node.description, "Declarations"].join(" "), "Declarations"),
|
|
9176
|
+
metadata: { ...node.metadata, organizerRepair: "semantic_page_grouping" }
|
|
9177
|
+
};
|
|
9178
|
+
}
|
|
9179
|
+
if (node.kind === "page" && looksLikePolicyFormStart(node)) {
|
|
9180
|
+
return {
|
|
9181
|
+
...node,
|
|
9182
|
+
title: "Policy Form",
|
|
9183
|
+
description: cleanText([node.description, "Policy Form"].join(" "), "Policy Form"),
|
|
9184
|
+
metadata: { ...node.metadata, organizerRepair: "semantic_page_grouping" }
|
|
9185
|
+
};
|
|
9186
|
+
}
|
|
9187
|
+
return node;
|
|
9188
|
+
});
|
|
9189
|
+
const rootId = sourceTreeRootId(relabeled);
|
|
9190
|
+
const children = (nodesByParent(relabeled).get(rootId) ?? []).filter((node) => node.kind !== "document").sort((left, right) => left.order - right.order);
|
|
9191
|
+
let nextTree = relabeled;
|
|
9192
|
+
const declarationsStartIndex = children.findIndex(looksLikeDeclarationsStart);
|
|
9193
|
+
if (declarationsStartIndex >= 0) {
|
|
9194
|
+
const declarationIds = [];
|
|
9195
|
+
for (let index = declarationsStartIndex; index < children.length; index += 1) {
|
|
9196
|
+
const child = children[index];
|
|
9197
|
+
if (index > declarationsStartIndex && (looksLikePolicyFormStart(child) || endorsementTitle(sourceNodeText(child)))) break;
|
|
9198
|
+
if (!looksLikeDeclarationsContinuation(child)) break;
|
|
9199
|
+
declarationIds.push(child.id);
|
|
9200
|
+
}
|
|
9201
|
+
nextTree = groupAdjacentChildren({
|
|
9202
|
+
sourceTree: nextTree,
|
|
9203
|
+
children,
|
|
9204
|
+
childIds: declarationIds,
|
|
9205
|
+
kind: "page_group",
|
|
9206
|
+
title: "Declarations",
|
|
9207
|
+
description: "Declarations pages and schedules grouped by source order",
|
|
9208
|
+
organizer: "semantic_declarations_grouping"
|
|
9209
|
+
});
|
|
9210
|
+
}
|
|
9211
|
+
const policyStartIndex = children.findIndex(looksLikePolicyFormStart);
|
|
9212
|
+
if (policyStartIndex >= 0) {
|
|
9213
|
+
const policyIds = [];
|
|
9214
|
+
for (let index = policyStartIndex; index < children.length; index += 1) {
|
|
9215
|
+
const child = children[index];
|
|
9216
|
+
if (index > policyStartIndex && endorsementTitle(sourceNodeText(child))) break;
|
|
9217
|
+
if (!looksLikePolicyFormContinuation(child)) break;
|
|
9218
|
+
policyIds.push(child.id);
|
|
9219
|
+
}
|
|
9220
|
+
nextTree = groupAdjacentChildren({
|
|
9221
|
+
sourceTree: nextTree,
|
|
9222
|
+
children,
|
|
9223
|
+
childIds: policyIds,
|
|
9224
|
+
kind: "form",
|
|
9225
|
+
title: "Policy Form",
|
|
9226
|
+
description: "Policy form pages grouped by source order",
|
|
9227
|
+
organizer: "semantic_policy_form_grouping"
|
|
9228
|
+
});
|
|
9229
|
+
}
|
|
9230
|
+
return applyEndorsementGrouping(normalizeDocumentSourceTreePaths(nextTree));
|
|
9081
9231
|
}
|
|
9082
9232
|
function rejectsOrganizerGroup(group, children) {
|
|
9233
|
+
if (/^endorsements?\b/i.test(group.title) && group.kind !== "page_group") return true;
|
|
9083
9234
|
if (group.kind !== "endorsement") return false;
|
|
9084
9235
|
if (/^endorsements?\s+\d+\s*[–-]\s*\d+\b/i.test(group.title)) return true;
|
|
9085
9236
|
const childNumbers = new Set(
|
|
@@ -9087,6 +9238,120 @@ function rejectsOrganizerGroup(group, children) {
|
|
|
9087
9238
|
);
|
|
9088
9239
|
return childNumbers.size > 1;
|
|
9089
9240
|
}
|
|
9241
|
+
function isEndorsementGroup(node) {
|
|
9242
|
+
return node.kind === "page_group" && /^endorsements?\b/i.test(node.title);
|
|
9243
|
+
}
|
|
9244
|
+
function endorsementGroupNodeId(documentId, parentId) {
|
|
9245
|
+
return [
|
|
9246
|
+
documentId.replace(/[^a-zA-Z0-9_.:-]/g, "_"),
|
|
9247
|
+
"source_node",
|
|
9248
|
+
"page_group",
|
|
9249
|
+
"endorsements",
|
|
9250
|
+
parentId?.replace(/[^a-zA-Z0-9_.:-]/g, "_").slice(0, 48) ?? "root"
|
|
9251
|
+
].join(":");
|
|
9252
|
+
}
|
|
9253
|
+
function applyEndorsementGrouping(sourceTree) {
|
|
9254
|
+
const relabeledTree = sourceTree.map((node) => {
|
|
9255
|
+
if (node.kind === "document" || isEndorsementGroup(node) || node.kind === "endorsement") return node;
|
|
9256
|
+
const title = endorsementTitle([node.title, node.description, node.textExcerpt].filter(Boolean).join(" "));
|
|
9257
|
+
if (!title) return node;
|
|
9258
|
+
return {
|
|
9259
|
+
...node,
|
|
9260
|
+
kind: "endorsement",
|
|
9261
|
+
title,
|
|
9262
|
+
description: cleanText(
|
|
9263
|
+
[title, "endorsement", node.pageStart ? `page ${node.pageStart}` : void 0, node.textExcerpt].filter(Boolean).join(" | "),
|
|
9264
|
+
title
|
|
9265
|
+
),
|
|
9266
|
+
metadata: {
|
|
9267
|
+
...node.metadata,
|
|
9268
|
+
organizerRepair: "normalize_endorsement_grouping"
|
|
9269
|
+
}
|
|
9270
|
+
};
|
|
9271
|
+
});
|
|
9272
|
+
const byParent = nodesByParent(relabeledTree);
|
|
9273
|
+
const groupsByParent = /* @__PURE__ */ new Map();
|
|
9274
|
+
const endorsementGroupIds = new Set(
|
|
9275
|
+
relabeledTree.filter(isEndorsementGroup).map((node) => node.id)
|
|
9276
|
+
);
|
|
9277
|
+
let nextTree = relabeledTree.map((node) => {
|
|
9278
|
+
if (!isEndorsementGroup(node)) return node;
|
|
9279
|
+
const normalized = {
|
|
9280
|
+
...node,
|
|
9281
|
+
kind: "page_group",
|
|
9282
|
+
title: "Endorsements",
|
|
9283
|
+
description: cleanText(node.description, "Endorsement forms grouped by source order"),
|
|
9284
|
+
metadata: {
|
|
9285
|
+
...node.metadata,
|
|
9286
|
+
sourceTreeVersion: "v3",
|
|
9287
|
+
organizer: node.metadata?.organizer ?? "endorsement_grouping"
|
|
9288
|
+
}
|
|
9289
|
+
};
|
|
9290
|
+
groupsByParent.set(node.parentId, normalized);
|
|
9291
|
+
endorsementGroupIds.add(node.id);
|
|
9292
|
+
return normalized;
|
|
9293
|
+
});
|
|
9294
|
+
nextTree = nextTree.map((node) => {
|
|
9295
|
+
if (!endorsementGroupIds.has(node.parentId ?? "")) return node;
|
|
9296
|
+
const title = endorsementTitle([node.title, node.description, node.textExcerpt].filter(Boolean).join(" "));
|
|
9297
|
+
if (!title) return node;
|
|
9298
|
+
return {
|
|
9299
|
+
...node,
|
|
9300
|
+
kind: "endorsement",
|
|
9301
|
+
title,
|
|
9302
|
+
description: cleanText(
|
|
9303
|
+
[title, "endorsement", node.pageStart ? `page ${node.pageStart}` : void 0, node.textExcerpt].filter(Boolean).join(" | "),
|
|
9304
|
+
title
|
|
9305
|
+
),
|
|
9306
|
+
metadata: {
|
|
9307
|
+
...node.metadata,
|
|
9308
|
+
organizerRepair: "normalize_endorsement_grouping"
|
|
9309
|
+
}
|
|
9310
|
+
};
|
|
9311
|
+
});
|
|
9312
|
+
for (const [parentId, children] of byParent) {
|
|
9313
|
+
if (endorsementGroupIds.has(parentId ?? "")) continue;
|
|
9314
|
+
const endorsementChildren = children.filter((child) => child.kind === "endorsement" && !isEndorsementGroup(child));
|
|
9315
|
+
if (endorsementChildren.length < 2) continue;
|
|
9316
|
+
const documentId = endorsementChildren[0].documentId;
|
|
9317
|
+
const pageStarts = endorsementChildren.map((child) => child.pageStart).filter((page) => typeof page === "number");
|
|
9318
|
+
const pageEnds = endorsementChildren.map((child) => child.pageEnd ?? child.pageStart).filter((page) => typeof page === "number");
|
|
9319
|
+
const order = Math.min(...endorsementChildren.map((child) => child.order));
|
|
9320
|
+
const existingGroup = groupsByParent.get(parentId);
|
|
9321
|
+
const groupId = existingGroup?.id ?? endorsementGroupNodeId(documentId, parentId);
|
|
9322
|
+
const groupNode = existingGroup ?? {
|
|
9323
|
+
id: groupId,
|
|
9324
|
+
documentId,
|
|
9325
|
+
parentId,
|
|
9326
|
+
kind: "page_group",
|
|
9327
|
+
title: "Endorsements",
|
|
9328
|
+
description: "Endorsement forms grouped by source order",
|
|
9329
|
+
textExcerpt: void 0,
|
|
9330
|
+
sourceSpanIds: [],
|
|
9331
|
+
pageStart: pageStarts.length ? Math.min(...pageStarts) : void 0,
|
|
9332
|
+
pageEnd: pageEnds.length ? Math.max(...pageEnds) : void 0,
|
|
9333
|
+
bbox: endorsementChildren.flatMap((child) => child.bbox ?? []).slice(0, 12),
|
|
9334
|
+
order,
|
|
9335
|
+
path: "",
|
|
9336
|
+
metadata: { sourceTreeVersion: "v3", organizer: "endorsement_grouping" }
|
|
9337
|
+
};
|
|
9338
|
+
const childSpanIds = [...new Set(endorsementChildren.flatMap((child) => child.sourceSpanIds))];
|
|
9339
|
+
const normalizedGroup = {
|
|
9340
|
+
...groupNode,
|
|
9341
|
+
sourceSpanIds: groupNode.sourceSpanIds.length ? groupNode.sourceSpanIds : childSpanIds,
|
|
9342
|
+
pageStart: groupNode.pageStart ?? (pageStarts.length ? Math.min(...pageStarts) : void 0),
|
|
9343
|
+
pageEnd: groupNode.pageEnd ?? (pageEnds.length ? Math.max(...pageEnds) : void 0),
|
|
9344
|
+
order
|
|
9345
|
+
};
|
|
9346
|
+
groupsByParent.set(parentId, normalizedGroup);
|
|
9347
|
+
if (!existingGroup) nextTree.push(normalizedGroup);
|
|
9348
|
+
else nextTree = nextTree.map((node) => node.id === normalizedGroup.id ? normalizedGroup : node);
|
|
9349
|
+
nextTree = nextTree.map(
|
|
9350
|
+
(node) => endorsementChildren.some((child) => child.id === node.id) ? { ...node, parentId: groupId, order: node.order + 1e-3 } : node
|
|
9351
|
+
);
|
|
9352
|
+
}
|
|
9353
|
+
return normalizeDocumentSourceTreePaths(nextTree);
|
|
9354
|
+
}
|
|
9090
9355
|
function compactNode(node, maxText = 700) {
|
|
9091
9356
|
return {
|
|
9092
9357
|
id: node.id,
|
|
@@ -9175,7 +9440,8 @@ Rules:
|
|
|
9175
9440
|
- Use only node IDs from the provided list.
|
|
9176
9441
|
- Do not invent text, page numbers, source spans, limits, or policy facts.
|
|
9177
9442
|
- You may relabel existing nodes and group adjacent top-level/page nodes from this batch only when they are clearly one continuous form, one declarations set, one schedule, or one clause family.
|
|
9178
|
-
-
|
|
9443
|
+
- Group adjacent separately numbered endorsements under a single generic "Endorsements" page_group parent, with each individual endorsement preserved as its own child node.
|
|
9444
|
+
- Never create rollup titles such as "Endorsements 1-3 (...)" or merge multiple endorsements into one endorsement node.
|
|
9179
9445
|
- Add concise, human-readable titles to generic text, table, row, and cell nodes when the text makes their role clear.
|
|
9180
9446
|
- Keep organizer titles terse. Use the printed heading or a compact canonical title such as "Declarations", "Policy Form", "Definitions", or "Endorsement No. 3"; do not add parenthetical summaries.
|
|
9181
9447
|
- Groups must list existing childNodeIds only.
|
|
@@ -9270,7 +9536,7 @@ function applyOrganization(sourceTree, organization) {
|
|
|
9270
9536
|
];
|
|
9271
9537
|
byId.set(id, node);
|
|
9272
9538
|
}
|
|
9273
|
-
return normalizeDocumentSourceTreePaths(nextTree);
|
|
9539
|
+
return applyEndorsementGrouping(normalizeDocumentSourceTreePaths(nextTree));
|
|
9274
9540
|
}
|
|
9275
9541
|
function sourceTreeToOutline(sourceTree) {
|
|
9276
9542
|
const byParent = /* @__PURE__ */ new Map();
|
|
@@ -9449,6 +9715,7 @@ async function runSourceTreeExtraction(params) {
|
|
|
9449
9715
|
} catch (error) {
|
|
9450
9716
|
warnings.push(`Source-tree organizer failed; deterministic tree used (${error instanceof Error ? error.message : String(error)})`);
|
|
9451
9717
|
}
|
|
9718
|
+
sourceTree = applySemanticPageGrouping(sourceTree);
|
|
9452
9719
|
const deterministicProfile = buildDeterministicOperationalProfile({
|
|
9453
9720
|
sourceTree,
|
|
9454
9721
|
sourceSpans
|