@claritylabs/cl-sdk 2.0.1 → 3.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -107,6 +107,8 @@ __export(index_exports, {
107
107
  DocumentMetadataSchema: () => DocumentMetadataSchema,
108
108
  DocumentNodeSchema: () => DocumentNodeSchema,
109
109
  DocumentPageMapEntrySchema: () => DocumentPageMapEntrySchema,
110
+ DocumentSourceNodeKindSchema: () => DocumentSourceNodeKindSchema,
111
+ DocumentSourceNodeSchema: () => DocumentSourceNodeSchema,
110
112
  DocumentTableOfContentsEntrySchema: () => DocumentTableOfContentsEntrySchema,
111
113
  DocumentTypeSchema: () => DocumentTypeSchema,
112
114
  DriverRecordSchema: () => DriverRecordSchema,
@@ -162,6 +164,9 @@ __export(index_exports, {
162
164
  MemorySourceStore: () => MemorySourceStore,
163
165
  MissingInfoQuestionSchema: () => MissingInfoQuestionSchema,
164
166
  NamedInsuredSchema: () => NamedInsuredSchema,
167
+ OperationalCoverageLineSchema: () => OperationalCoverageLineSchema,
168
+ OperationalEndorsementSupportSchema: () => OperationalEndorsementSupportSchema,
169
+ OperationalPartySchema: () => OperationalPartySchema,
165
170
  PERSONAL_AUTO_USAGES: () => PERSONAL_AUTO_USAGES,
166
171
  PET_SPECIES: () => PET_SPECIES,
167
172
  PLATFORM_CONFIGS: () => PLATFORM_CONFIGS,
@@ -191,6 +196,7 @@ __export(index_exports, {
191
196
  PolicyChangeStatusSchema: () => PolicyChangeStatusSchema,
192
197
  PolicyConditionSchema: () => PolicyConditionSchema,
193
198
  PolicyDocumentSchema: () => PolicyDocumentSchema,
199
+ PolicyOperationalProfileSchema: () => PolicyOperationalProfileSchema,
194
200
  PolicySectionTypeSchema: () => PolicySectionTypeSchema,
195
201
  PolicyTermTypeSchema: () => PolicyTermTypeSchema,
196
202
  PolicyTypeSchema: () => PolicyTypeSchema,
@@ -222,6 +228,7 @@ __export(index_exports, {
222
228
  ScheduledItemCategorySchema: () => ScheduledItemCategorySchema,
223
229
  SectionSchema: () => SectionSchema,
224
230
  SharedLimitSchema: () => SharedLimitSchema,
231
+ SourceBackedValueSchema: () => SourceBackedValueSchema,
225
232
  SourceChunkSchema: () => SourceChunkSchema,
226
233
  SourceKindSchema: () => SourceKindSchema,
227
234
  SourceSpanBBoxSchema: () => SourceSpanBBoxSchema,
@@ -263,7 +270,9 @@ __export(index_exports, {
263
270
  buildConfirmationSummaryPrompt: () => buildConfirmationSummaryPrompt,
264
271
  buildConversationMemoryGuidance: () => buildConversationMemoryGuidance,
265
272
  buildCoverageGapPrompt: () => buildCoverageGapPrompt,
273
+ buildDeterministicOperationalProfile: () => buildDeterministicOperationalProfile,
266
274
  buildDoclingProviderOptions: () => buildDoclingProviderOptions,
275
+ buildDocumentSourceTree: () => buildDocumentSourceTree,
267
276
  buildFieldExplanationPrompt: () => buildFieldExplanationPrompt,
268
277
  buildFieldExtractionPrompt: () => buildFieldExtractionPrompt,
269
278
  buildFlatPdfMappingPrompt: () => buildFlatPdfMappingPrompt,
@@ -312,9 +321,11 @@ __export(index_exports, {
312
321
  getTemplate: () => getTemplate,
313
322
  isDoclingExtractionInput: () => isDoclingExtractionInput,
314
323
  isFileReference: () => isFileReference,
324
+ mergeOperationalProfile: () => mergeOperationalProfile,
315
325
  mergeQuestionAnswers: () => mergeQuestionAnswers,
316
326
  mergeSourceSpans: () => mergeSourceSpans,
317
327
  normalizeDoclingDocument: () => normalizeDoclingDocument,
328
+ normalizeDocumentSourceTreePaths: () => normalizeDocumentSourceTreePaths,
318
329
  normalizeForMatch: () => normalizeForMatch,
319
330
  orderSourceEvidence: () => orderSourceEvidence,
320
331
  overlayTextOnPdf: () => overlayTextOnPdf,
@@ -1750,6 +1761,8 @@ var DocumentAgentGuidanceSchema = import_zod16.z.object({
1750
1761
  sourceSpanIds: import_zod16.z.array(import_zod16.z.string()).optional()
1751
1762
  });
1752
1763
  var DocumentMetadataSchema = import_zod16.z.object({
1764
+ sourceTreeVersion: import_zod16.z.string().optional(),
1765
+ sourceTreeCanonical: import_zod16.z.boolean().optional(),
1753
1766
  formInventory: import_zod16.z.array(FormReferenceSchema).optional(),
1754
1767
  tableOfContents: import_zod16.z.array(DocumentTableOfContentsEntrySchema).optional(),
1755
1768
  pageMap: import_zod16.z.array(DocumentPageMapEntrySchema).optional(),
@@ -2349,6 +2362,86 @@ var SourceChunkSchema = import_zod20.z.object({
2349
2362
  pageEnd: import_zod20.z.number().int().positive().optional(),
2350
2363
  metadata: import_zod20.z.record(import_zod20.z.string(), import_zod20.z.string()).default({})
2351
2364
  });
2365
+ var DocumentSourceNodeKindSchema = import_zod20.z.enum([
2366
+ "document",
2367
+ "page_group",
2368
+ "page",
2369
+ "form",
2370
+ "endorsement",
2371
+ "section",
2372
+ "schedule",
2373
+ "clause",
2374
+ "table",
2375
+ "table_row",
2376
+ "table_cell",
2377
+ "text"
2378
+ ]);
2379
+ var DocumentSourceNodeSchema = import_zod20.z.object({
2380
+ id: import_zod20.z.string().min(1),
2381
+ documentId: import_zod20.z.string().min(1),
2382
+ parentId: import_zod20.z.string().optional(),
2383
+ kind: DocumentSourceNodeKindSchema,
2384
+ title: import_zod20.z.string(),
2385
+ description: import_zod20.z.string(),
2386
+ textExcerpt: import_zod20.z.string().optional(),
2387
+ sourceSpanIds: import_zod20.z.array(import_zod20.z.string().min(1)),
2388
+ pageStart: import_zod20.z.number().int().positive().optional(),
2389
+ pageEnd: import_zod20.z.number().int().positive().optional(),
2390
+ bbox: import_zod20.z.array(SourceSpanBBoxSchema).optional(),
2391
+ order: import_zod20.z.number().int().nonnegative(),
2392
+ path: import_zod20.z.string(),
2393
+ metadata: import_zod20.z.record(import_zod20.z.string(), import_zod20.z.unknown()).optional()
2394
+ });
2395
+ var SourceBackedValueSchema = import_zod20.z.object({
2396
+ value: import_zod20.z.string(),
2397
+ normalizedValue: import_zod20.z.string().optional(),
2398
+ confidence: import_zod20.z.enum(["low", "medium", "high"]).default("medium"),
2399
+ sourceNodeIds: import_zod20.z.array(import_zod20.z.string().min(1)).default([]),
2400
+ sourceSpanIds: import_zod20.z.array(import_zod20.z.string().min(1)).default([])
2401
+ });
2402
+ var OperationalCoverageLineSchema = import_zod20.z.object({
2403
+ name: import_zod20.z.string(),
2404
+ coverageCode: import_zod20.z.string().optional(),
2405
+ limit: import_zod20.z.string().optional(),
2406
+ deductible: import_zod20.z.string().optional(),
2407
+ premium: import_zod20.z.string().optional(),
2408
+ formNumber: import_zod20.z.string().optional(),
2409
+ sectionRef: import_zod20.z.string().optional(),
2410
+ sourceNodeIds: import_zod20.z.array(import_zod20.z.string().min(1)).default([]),
2411
+ sourceSpanIds: import_zod20.z.array(import_zod20.z.string().min(1)).default([])
2412
+ });
2413
+ var OperationalPartySchema = import_zod20.z.object({
2414
+ role: import_zod20.z.string(),
2415
+ name: import_zod20.z.string(),
2416
+ sourceNodeIds: import_zod20.z.array(import_zod20.z.string().min(1)).default([]),
2417
+ sourceSpanIds: import_zod20.z.array(import_zod20.z.string().min(1)).default([])
2418
+ });
2419
+ var OperationalEndorsementSupportSchema = import_zod20.z.object({
2420
+ kind: import_zod20.z.string(),
2421
+ status: import_zod20.z.enum(["supported", "excluded", "requires_review"]),
2422
+ summary: import_zod20.z.string(),
2423
+ sourceNodeIds: import_zod20.z.array(import_zod20.z.string().min(1)).default([]),
2424
+ sourceSpanIds: import_zod20.z.array(import_zod20.z.string().min(1)).default([])
2425
+ });
2426
+ var PolicyOperationalProfileSchema = import_zod20.z.object({
2427
+ documentType: import_zod20.z.enum(["policy", "quote"]).default("policy"),
2428
+ policyTypes: import_zod20.z.array(import_zod20.z.string()).default(["other"]),
2429
+ policyNumber: SourceBackedValueSchema.optional(),
2430
+ namedInsured: SourceBackedValueSchema.optional(),
2431
+ insurer: SourceBackedValueSchema.optional(),
2432
+ broker: SourceBackedValueSchema.optional(),
2433
+ effectiveDate: SourceBackedValueSchema.optional(),
2434
+ expirationDate: SourceBackedValueSchema.optional(),
2435
+ retroactiveDate: SourceBackedValueSchema.optional(),
2436
+ premium: SourceBackedValueSchema.optional(),
2437
+ coverageTypes: import_zod20.z.array(import_zod20.z.string()).default([]),
2438
+ coverages: import_zod20.z.array(OperationalCoverageLineSchema).default([]),
2439
+ parties: import_zod20.z.array(OperationalPartySchema).default([]),
2440
+ endorsementSupport: import_zod20.z.array(OperationalEndorsementSupportSchema).default([]),
2441
+ sourceNodeIds: import_zod20.z.array(import_zod20.z.string().min(1)).default([]),
2442
+ sourceSpanIds: import_zod20.z.array(import_zod20.z.string().min(1)).default([]),
2443
+ warnings: import_zod20.z.array(import_zod20.z.string()).default([])
2444
+ });
2352
2445
 
2353
2446
  // src/source/ids.ts
2354
2447
  function normalizeText(text) {
@@ -2532,8 +2625,8 @@ function chunkSourceSpans(spans, options = {}) {
2532
2625
  if (current.length === 0) return;
2533
2626
  const text = current.map((span) => span.text).join("\n\n");
2534
2627
  const textHash = sourceSpanTextHash(text);
2535
- const pageStart = firstNumber(current.map((span) => span.pageStart));
2536
- const pageEnd = lastNumber(current.map((span) => span.pageEnd ?? span.pageStart));
2628
+ const pageStart2 = firstNumber(current.map((span) => span.pageStart));
2629
+ const pageEnd2 = lastNumber(current.map((span) => span.pageEnd ?? span.pageStart));
2537
2630
  const chunk = {
2538
2631
  id: `${sanitizeIdPart(current[0].documentId)}:source_chunk:${chunks.length}:${stableHash2({
2539
2632
  sourceSpanIds: current.map((span) => span.id),
@@ -2543,8 +2636,8 @@ function chunkSourceSpans(spans, options = {}) {
2543
2636
  sourceSpanIds: current.map((span) => span.id),
2544
2637
  text,
2545
2638
  textHash,
2546
- pageStart,
2547
- pageEnd,
2639
+ pageStart: pageStart2,
2640
+ pageEnd: pageEnd2,
2548
2641
  metadata: mergeMetadata(current)
2549
2642
  };
2550
2643
  chunks.push(SourceChunkSchema.parse(chunk));
@@ -2697,6 +2790,542 @@ function matchesFilters(span, filters) {
2697
2790
  return true;
2698
2791
  }
2699
2792
 
2793
+ // src/source/tree.ts
2794
+ function normalizeWhitespace2(value) {
2795
+ return value.replace(/\s+/g, " ").trim();
2796
+ }
2797
+ function sanitizeIdPart2(value) {
2798
+ return value.replace(/[^a-zA-Z0-9_.:-]/g, "_");
2799
+ }
2800
+ function truncate(value, maxChars) {
2801
+ const text = normalizeWhitespace2(value);
2802
+ return text.length > maxChars ? `${text.slice(0, maxChars).trimEnd()}...` : text;
2803
+ }
2804
+ function pageStart(span) {
2805
+ return span.pageStart ?? span.location?.page ?? span.location?.startPage;
2806
+ }
2807
+ function pageEnd(span) {
2808
+ return span.pageEnd ?? span.location?.endPage ?? pageStart(span);
2809
+ }
2810
+ function sourceUnit2(span) {
2811
+ return span.sourceUnit ?? span.metadata?.sourceUnit ?? span.metadata?.elementType;
2812
+ }
2813
+ function elementType(span) {
2814
+ return span.metadata?.elementType ?? span.metadata?.sourceUnit ?? span.sourceUnit;
2815
+ }
2816
+ function tableId(span) {
2817
+ return span.table?.tableId ?? span.metadata?.tableId;
2818
+ }
2819
+ function rowSpanId(span) {
2820
+ return span.parentSpanId ?? span.table?.rowSpanId ?? span.metadata?.rowSpanId;
2821
+ }
2822
+ function nodeId(documentId, kind, parts) {
2823
+ return [
2824
+ sanitizeIdPart2(documentId),
2825
+ "source_node",
2826
+ kind,
2827
+ stableHash2(parts.filter((part) => part !== void 0).join("|")).slice(0, 12)
2828
+ ].join(":");
2829
+ }
2830
+ function titleCase(value) {
2831
+ return value.replace(/[_-]+/g, " ").replace(/\b\w/g, (char) => char.toUpperCase()).trim();
2832
+ }
2833
+ function nodeTextDescription(params) {
2834
+ return [
2835
+ params.title,
2836
+ params.kind.replace(/_/g, " "),
2837
+ params.page ? `page ${params.page}` : void 0,
2838
+ params.formNumber ? `form ${params.formNumber}` : void 0,
2839
+ params.text ? truncate(params.text, 1200) : void 0
2840
+ ].filter(Boolean).join(" | ");
2841
+ }
2842
+ function normalizeNodeKind(span) {
2843
+ const unit = sourceUnit2(span);
2844
+ const element = elementType(span);
2845
+ if (unit === "page") return "page";
2846
+ if (unit === "table") return "table";
2847
+ if (unit === "table_row") return "table_row";
2848
+ if (unit === "table_cell") return "table_cell";
2849
+ if (unit === "key_value") return "schedule";
2850
+ if (unit === "section") return "section";
2851
+ if (element === "title" || element === "section_candidate") {
2852
+ const text = span.text.toLowerCase();
2853
+ if (/endorsement/.test(text)) return "endorsement";
2854
+ if (/schedule|declarations?/.test(text)) return "schedule";
2855
+ if (/clause|condition|exclusion|definition/.test(text)) return "clause";
2856
+ return "section";
2857
+ }
2858
+ return "text";
2859
+ }
2860
+ function pageNodeTitle(page) {
2861
+ return `Page ${page}`;
2862
+ }
2863
+ function makeNode(params) {
2864
+ return {
2865
+ id: params.id,
2866
+ documentId: params.documentId,
2867
+ parentId: params.parentId,
2868
+ kind: params.kind,
2869
+ title: params.title,
2870
+ description: params.description ?? nodeTextDescription({
2871
+ kind: params.kind,
2872
+ title: params.title,
2873
+ text: params.textExcerpt,
2874
+ page: params.pageStart,
2875
+ formNumber: typeof params.metadata?.formNumber === "string" ? params.metadata.formNumber : void 0
2876
+ }),
2877
+ textExcerpt: params.textExcerpt,
2878
+ sourceSpanIds: params.sourceSpanIds ?? [],
2879
+ pageStart: params.pageStart,
2880
+ pageEnd: params.pageEnd,
2881
+ bbox: params.bbox,
2882
+ order: params.order,
2883
+ path: "",
2884
+ metadata: params.metadata
2885
+ };
2886
+ }
2887
+ function sortSpans(left, right) {
2888
+ const leftPage = pageStart(left) ?? 0;
2889
+ const rightPage = pageStart(right) ?? 0;
2890
+ if (leftPage !== rightPage) return leftPage - rightPage;
2891
+ const leftRow = left.table?.rowIndex ?? Number(left.metadata?.rowIndex ?? 0);
2892
+ const rightRow = right.table?.rowIndex ?? Number(right.metadata?.rowIndex ?? 0);
2893
+ if (leftRow !== rightRow) return leftRow - rightRow;
2894
+ const leftCol = left.table?.columnIndex ?? Number(left.metadata?.columnIndex ?? 0);
2895
+ const rightCol = right.table?.columnIndex ?? Number(right.metadata?.columnIndex ?? 0);
2896
+ if (leftCol !== rightCol) return leftCol - rightCol;
2897
+ return left.id.localeCompare(right.id);
2898
+ }
2899
+ function normalizeDocumentSourceTreePaths(nodes) {
2900
+ const byParent = /* @__PURE__ */ new Map();
2901
+ for (const node of nodes) {
2902
+ const key = node.parentId;
2903
+ const group = byParent.get(key) ?? [];
2904
+ group.push(node);
2905
+ byParent.set(key, group);
2906
+ }
2907
+ for (const group of byParent.values()) {
2908
+ group.sort((left, right) => left.order - right.order || left.id.localeCompare(right.id));
2909
+ }
2910
+ const result = [];
2911
+ const visit = (node, path) => {
2912
+ const next = { ...node, path };
2913
+ result.push(next);
2914
+ const children = byParent.get(node.id) ?? [];
2915
+ children.forEach((child, index) => visit(child, `${path}.${index + 1}`));
2916
+ };
2917
+ const roots = byParent.get(void 0) ?? [];
2918
+ roots.forEach((root, index) => visit(root, String(index + 1)));
2919
+ return result;
2920
+ }
2921
+ function buildDocumentSourceTree(sourceSpans, documentId) {
2922
+ const orderedSpans = [...sourceSpans].sort(sortSpans);
2923
+ const resolvedDocumentId = documentId ?? orderedSpans[0]?.documentId ?? "document";
2924
+ const nodes = /* @__PURE__ */ new Map();
2925
+ let order = 0;
2926
+ const rootId = nodeId(resolvedDocumentId, "document", [resolvedDocumentId]);
2927
+ nodes.set(rootId, makeNode({
2928
+ id: rootId,
2929
+ documentId: resolvedDocumentId,
2930
+ kind: "document",
2931
+ title: "Document",
2932
+ description: "Document root for source-native policy hierarchy",
2933
+ sourceSpanIds: orderedSpans.map((span) => span.id).slice(0, 200),
2934
+ pageStart: orderedSpans.map(pageStart).find((value) => typeof value === "number"),
2935
+ pageEnd: [...orderedSpans].reverse().map(pageEnd).find((value) => typeof value === "number"),
2936
+ order: order++,
2937
+ metadata: { sourceTreeVersion: "v3" }
2938
+ }));
2939
+ const pageNodeIds = /* @__PURE__ */ new Map();
2940
+ const tableNodeIds = /* @__PURE__ */ new Map();
2941
+ const rowNodeIds = /* @__PURE__ */ new Map();
2942
+ const ensurePage = (page) => {
2943
+ const existing = pageNodeIds.get(page);
2944
+ if (existing) return existing;
2945
+ const id = nodeId(resolvedDocumentId, "page", [page]);
2946
+ const pageSpan = orderedSpans.find((span) => pageStart(span) === page && sourceUnit2(span) === "page");
2947
+ nodes.set(id, makeNode({
2948
+ id,
2949
+ documentId: resolvedDocumentId,
2950
+ parentId: rootId,
2951
+ kind: "page",
2952
+ title: pageNodeTitle(page),
2953
+ description: pageSpan ? nodeTextDescription({ kind: "page", title: pageNodeTitle(page), text: pageSpan.text, page }) : pageNodeTitle(page),
2954
+ textExcerpt: pageSpan ? truncate(pageSpan.text, 1600) : void 0,
2955
+ sourceSpanIds: pageSpan ? [pageSpan.id] : [],
2956
+ pageStart: page,
2957
+ pageEnd: page,
2958
+ bbox: pageSpan?.bbox,
2959
+ order: order++,
2960
+ metadata: { sourceUnit: "page" }
2961
+ }));
2962
+ pageNodeIds.set(page, id);
2963
+ return id;
2964
+ };
2965
+ const ensureTable = (span, pageParentId) => {
2966
+ const idSource = tableId(span) ?? `${span.documentId}:p${pageStart(span) ?? "na"}:table:${nodes.size}`;
2967
+ const existing = tableNodeIds.get(idSource);
2968
+ if (existing) return existing;
2969
+ const id = nodeId(resolvedDocumentId, "table", [idSource]);
2970
+ nodes.set(id, makeNode({
2971
+ id,
2972
+ documentId: resolvedDocumentId,
2973
+ parentId: pageParentId,
2974
+ kind: "table",
2975
+ title: `Table ${tableNodeIds.size + 1}`,
2976
+ description: `Table on page ${pageStart(span) ?? "unknown"} for source rows and cells`,
2977
+ sourceSpanIds: [],
2978
+ pageStart: pageStart(span),
2979
+ pageEnd: pageEnd(span),
2980
+ order: order++,
2981
+ metadata: { tableId: idSource, sourceUnit: "table" }
2982
+ }));
2983
+ tableNodeIds.set(idSource, id);
2984
+ return id;
2985
+ };
2986
+ const addStandaloneSpanNode = (span, parentId) => {
2987
+ const kind = normalizeNodeKind(span);
2988
+ if (kind === "page") return;
2989
+ const page = pageStart(span);
2990
+ const title = span.sectionId ?? span.formNumber ?? (kind === "table_cell" && span.table?.columnName ? String(span.table.columnName) : void 0) ?? titleCase(kind);
2991
+ const id = nodeId(resolvedDocumentId, kind, [span.id]);
2992
+ nodes.set(id, makeNode({
2993
+ id,
2994
+ documentId: resolvedDocumentId,
2995
+ parentId,
2996
+ kind,
2997
+ title,
2998
+ description: nodeTextDescription({ kind, title, text: span.text, page, formNumber: span.formNumber }),
2999
+ textExcerpt: truncate(span.text, 1600),
3000
+ sourceSpanIds: [span.id],
3001
+ pageStart: page,
3002
+ pageEnd: pageEnd(span),
3003
+ bbox: span.bbox,
3004
+ order: order++,
3005
+ metadata: {
3006
+ ...span.metadata ?? {},
3007
+ formNumber: span.formNumber,
3008
+ sourceUnit: sourceUnit2(span)
3009
+ }
3010
+ }));
3011
+ };
3012
+ for (const span of orderedSpans) {
3013
+ const page = pageStart(span);
3014
+ const pageParentId = page ? ensurePage(page) : rootId;
3015
+ const kind = normalizeNodeKind(span);
3016
+ if (kind === "page") continue;
3017
+ if (kind === "table_row") {
3018
+ const tableParentId = ensureTable(span, pageParentId);
3019
+ const rowKey = span.id;
3020
+ const id = nodeId(resolvedDocumentId, "table_row", [span.id]);
3021
+ nodes.set(id, makeNode({
3022
+ id,
3023
+ documentId: resolvedDocumentId,
3024
+ parentId: tableParentId,
3025
+ kind,
3026
+ title: span.table?.isHeader ? "Header row" : `Row ${(span.table?.rowIndex ?? 0) + 1}`,
3027
+ description: nodeTextDescription({ kind, title: "Table row", text: span.text, page }),
3028
+ textExcerpt: truncate(span.text, 1600),
3029
+ sourceSpanIds: [span.id],
3030
+ pageStart: page,
3031
+ pageEnd: pageEnd(span),
3032
+ bbox: span.bbox,
3033
+ order: order++,
3034
+ metadata: {
3035
+ ...span.metadata ?? {},
3036
+ ...span.table ?? {},
3037
+ sourceUnit: "table_row"
3038
+ }
3039
+ }));
3040
+ rowNodeIds.set(rowKey, id);
3041
+ continue;
3042
+ }
3043
+ if (kind === "table_cell") {
3044
+ const tableParentId = ensureTable(span, pageParentId);
3045
+ const parentRowId2 = rowSpanId(span);
3046
+ const parentId = parentRowId2 ? rowNodeIds.get(parentRowId2) ?? tableParentId : tableParentId;
3047
+ addStandaloneSpanNode(span, parentId);
3048
+ continue;
3049
+ }
3050
+ addStandaloneSpanNode(span, pageParentId);
3051
+ }
3052
+ return normalizeDocumentSourceTreePaths([...nodes.values()]);
3053
+ }
3054
+
3055
+ // src/source/operational-profile.ts
3056
+ function normalizeWhitespace3(value) {
3057
+ return value.replace(/\s+/g, " ").trim();
3058
+ }
3059
+ function cleanValue(value) {
3060
+ if (!value) return void 0;
3061
+ return normalizeWhitespace3(value.replace(/^[\s:;#-]+|[\s;,.]+$/g, ""));
3062
+ }
3063
+ function moneyValue(value) {
3064
+ const clean = cleanValue(value);
3065
+ if (!clean) return void 0;
3066
+ if (/^\$/.test(clean)) return clean;
3067
+ if (/^\d{1,3}(?:,\d{3})*(?:\.\d{2})?$/.test(clean)) return `$${clean}`;
3068
+ return clean;
3069
+ }
3070
+ function nodeText(node) {
3071
+ return normalizeWhitespace3([
3072
+ node.title,
3073
+ node.description,
3074
+ node.textExcerpt
3075
+ ].filter(Boolean).join(" "));
3076
+ }
3077
+ function valueFromNode(node, value, confidence = "medium") {
3078
+ return {
3079
+ value,
3080
+ confidence,
3081
+ sourceNodeIds: [node.id],
3082
+ sourceSpanIds: node.sourceSpanIds
3083
+ };
3084
+ }
3085
+ function firstMatch(nodes, patterns) {
3086
+ for (const node of nodes) {
3087
+ const text = nodeText(node);
3088
+ for (const pattern of patterns) {
3089
+ const match = text.match(pattern);
3090
+ const value = cleanValue(match?.[1]);
3091
+ if (value) return valueFromNode(node, value, "high");
3092
+ }
3093
+ }
3094
+ return void 0;
3095
+ }
3096
+ function inferPolicyTypes(nodes) {
3097
+ const text = nodes.slice(0, 40).map(nodeText).join(" ").toLowerCase();
3098
+ const types = [];
3099
+ const add = (pattern, type) => {
3100
+ if (pattern.test(text) && !types.includes(type)) types.push(type);
3101
+ };
3102
+ add(/\b(cyber|network security|privacy liability|data breach)\b/i, "cyber");
3103
+ add(/\b(professional liability|errors?\s*&?\s*omissions|e&o)\b/i, "professional_liability");
3104
+ add(/\b(commercial general liability|general liability|cgl)\b/i, "general_liability");
3105
+ add(/\b(umbrella|excess liability)\b/i, "umbrella");
3106
+ add(/\b(workers'? compensation|employers'? liability)\b/i, "workers_comp");
3107
+ add(/\b(commercial auto|business auto|automobile liability)\b/i, "commercial_auto");
3108
+ add(/\b(commercial property|property coverage|building coverage)\b/i, "commercial_property");
3109
+ return types.length ? types : ["other"];
3110
+ }
3111
+ function inferDocumentType(nodes) {
3112
+ const text = nodes.slice(0, 25).map(nodeText).join(" ").toLowerCase();
3113
+ if (/\b(quote|proposal|quotation|indication)\b/.test(text) && !/\bpolicy number\b/.test(text)) {
3114
+ return "quote";
3115
+ }
3116
+ return "policy";
3117
+ }
3118
+ function coverageNameFromRow(text) {
3119
+ const labelled = text.match(/\b(?:coverage|coverage part|line)\s*:?\s*([^|;$]{3,80})/i)?.[1];
3120
+ if (labelled) return cleanValue(labelled);
3121
+ const parts = text.split(/\s+\|\s+| {2,}|\t/).map(cleanValue).filter(Boolean);
3122
+ const first = parts.find(
3123
+ (part) => !/^(limit|limits?|deductible|premium|amount|basis|rate|retroactive|aggregate|each occurrence)$/i.test(part) && !/^\$?[\d,]+/.test(part)
3124
+ );
3125
+ return cleanValue(first);
3126
+ }
3127
+ function limitFromText(text) {
3128
+ return moneyValue(
3129
+ text.match(/\b(?:limit|liability|aggregate|occurrence|claim)\s*:?\s*(\$?\d[\d,]*(?:\.\d{2})?|\$?\d[\d,]*\s*(?:each|per|aggregate)[^|;]*)/i)?.[1] ?? text.match(/(\$\s?\d[\d,]*(?:\.\d{2})?)(?=.*\b(limit|aggregate|occurrence|claim|liability)\b)/i)?.[1]
3130
+ );
3131
+ }
3132
+ function deductibleFromText(text) {
3133
+ return moneyValue(text.match(/\b(?:deductible|retention|sir)\s*:?\s*(\$?\d[\d,]*(?:\.\d{2})?)/i)?.[1]);
3134
+ }
3135
+ function premiumFromText(text) {
3136
+ return moneyValue(text.match(/\b(?:premium|total premium|total cost|amount due)\s*:?\s*(\$?\d[\d,]*(?:\.\d{2})?)/i)?.[1]);
3137
+ }
3138
+ function buildCoverages(nodes) {
3139
+ const rows = nodes.filter((node) => {
3140
+ const text = nodeText(node);
3141
+ return (node.kind === "table_row" || node.kind === "schedule" || node.kind === "text") && /\b(coverage|limit|deductible|aggregate|occurrence|liability|premium)\b/i.test(text);
3142
+ });
3143
+ const coverages = [];
3144
+ const seen = /* @__PURE__ */ new Set();
3145
+ for (const row of rows) {
3146
+ const text = nodeText(row);
3147
+ const name = coverageNameFromRow(text);
3148
+ const limit = limitFromText(text);
3149
+ const deductible = deductibleFromText(text);
3150
+ const premium = premiumFromText(text);
3151
+ if (!name || !limit && !deductible && !premium) continue;
3152
+ const key = [name.toLowerCase(), limit, deductible, premium].join("|");
3153
+ if (seen.has(key)) continue;
3154
+ seen.add(key);
3155
+ coverages.push({
3156
+ name,
3157
+ limit,
3158
+ deductible,
3159
+ premium,
3160
+ formNumber: typeof row.metadata?.formNumber === "string" ? row.metadata.formNumber : void 0,
3161
+ sourceNodeIds: [row.id],
3162
+ sourceSpanIds: row.sourceSpanIds
3163
+ });
3164
+ if (coverages.length >= 60) break;
3165
+ }
3166
+ return coverages;
3167
+ }
3168
+ function buildParties(profile) {
3169
+ const parties = [];
3170
+ if (profile.namedInsured) {
3171
+ parties.push({
3172
+ role: "named_insured",
3173
+ name: profile.namedInsured.value,
3174
+ sourceNodeIds: profile.namedInsured.sourceNodeIds,
3175
+ sourceSpanIds: profile.namedInsured.sourceSpanIds
3176
+ });
3177
+ }
3178
+ if (profile.insurer) {
3179
+ parties.push({
3180
+ role: "insurer",
3181
+ name: profile.insurer.value,
3182
+ sourceNodeIds: profile.insurer.sourceNodeIds,
3183
+ sourceSpanIds: profile.insurer.sourceSpanIds
3184
+ });
3185
+ }
3186
+ if (profile.broker) {
3187
+ parties.push({
3188
+ role: "broker",
3189
+ name: profile.broker.value,
3190
+ sourceNodeIds: profile.broker.sourceNodeIds,
3191
+ sourceSpanIds: profile.broker.sourceSpanIds
3192
+ });
3193
+ }
3194
+ return parties;
3195
+ }
3196
+ function buildEndorsementSupport(nodes) {
3197
+ const support = [];
3198
+ for (const node of nodes) {
3199
+ const text = nodeText(node);
3200
+ const add = (kind, status) => {
3201
+ if (support.some((item) => item.kind === kind && item.status === status && item.sourceNodeIds.includes(node.id))) {
3202
+ return;
3203
+ }
3204
+ support.push({
3205
+ kind,
3206
+ status,
3207
+ summary: node.textExcerpt ?? node.description,
3208
+ sourceNodeIds: [node.id],
3209
+ sourceSpanIds: node.sourceSpanIds
3210
+ });
3211
+ };
3212
+ if (/additional insured/i.test(text)) add("additional_insured", /not included|requires endorsement|only by endorsement/i.test(text) ? "requires_review" : "supported");
3213
+ if (/waiver of subrogation|subrogation.*waived/i.test(text)) add("waiver_of_subrogation", /not included|requires endorsement|only by endorsement/i.test(text) ? "requires_review" : "supported");
3214
+ if (/primary.*non[-\s]?contributory|non[-\s]?contributory/i.test(text)) add("primary_non_contributory", /not included|requires endorsement|only by endorsement/i.test(text) ? "requires_review" : "supported");
3215
+ if (/loss payee|mortgagee/i.test(text)) add(/mortgagee/i.test(text) ? "mortgagee" : "loss_payee", "supported");
3216
+ if (support.length >= 20) break;
3217
+ }
3218
+ return support;
3219
+ }
3220
+ function buildDeterministicOperationalProfile(params) {
3221
+ const nodes = params.sourceTree.filter((node) => node.kind !== "document");
3222
+ const partial = {
3223
+ documentType: inferDocumentType(nodes),
3224
+ policyTypes: inferPolicyTypes(nodes),
3225
+ policyNumber: firstMatch(nodes, [
3226
+ /\bpolicy\s*(?:number|no\.?|#)\s*:?\s*([A-Z0-9][A-Z0-9-]{4,})/i,
3227
+ /\bpolicy\s*[:#]\s*([A-Z0-9][A-Z0-9-]{4,})/i
3228
+ ]),
3229
+ namedInsured: firstMatch(nodes, [
3230
+ /\b(?:named insured|insured name|insured)\s*:?\s*(.+?)(?=\s+(?:coverage|policy number|insurer|carrier|premium|effective|expiration)\b|[|;\n]|$)/i,
3231
+ /\b(?:applicant|policyholder)\s*:?\s*(.+?)(?=\s+(?:coverage|policy number|insurer|carrier|premium|effective|expiration)\b|[|;\n]|$)/i
3232
+ ]),
3233
+ insurer: firstMatch(nodes, [
3234
+ /\b(?:insurer|carrier|company|security)\s*:?\s*([^|;\n]{3,120})/i,
3235
+ /\bunderwritten by\s+([^|;\n]{3,120})/i
3236
+ ]),
3237
+ broker: firstMatch(nodes, [
3238
+ /\b(?:broker|producer|agent)\s*:?\s*([^|;\n]{3,120})/i
3239
+ ]),
3240
+ effectiveDate: firstMatch(nodes, [
3241
+ /\b(?:effective date|policy period from|from)\s*:?\s*([0-9]{1,2}[/-][0-9]{1,2}[/-][0-9]{2,4})/i,
3242
+ /\b([0-9]{1,2}[/-][0-9]{1,2}[/-][0-9]{2,4})\s+(?:to|through|-)\s+[0-9]{1,2}[/-][0-9]{1,2}[/-][0-9]{2,4}/i
3243
+ ]),
3244
+ expirationDate: firstMatch(nodes, [
3245
+ /\b(?:expiration date|expiry date|expires|to)\s*:?\s*([0-9]{1,2}[/-][0-9]{1,2}[/-][0-9]{2,4})/i,
3246
+ /\b[0-9]{1,2}[/-][0-9]{1,2}[/-][0-9]{2,4}\s+(?:to|through|-)\s+([0-9]{1,2}[/-][0-9]{1,2}[/-][0-9]{2,4})/i
3247
+ ]),
3248
+ retroactiveDate: firstMatch(nodes, [
3249
+ /\bretroactive date\s*:?\s*([0-9]{1,2}[/-][0-9]{1,2}[/-][0-9]{2,4}|full prior acts|none)/i
3250
+ ]),
3251
+ premium: firstMatch(nodes, [
3252
+ /\b(?:total premium|premium|total cost|amount due)\s*:?\s*(\$?\d[\d,]*(?:\.\d{2})?)/i
3253
+ ])
3254
+ };
3255
+ const coverages = buildCoverages(nodes);
3256
+ const coverageTypes = [...new Set(coverages.map((coverage) => coverage.name))];
3257
+ const sourceNodeIds = [.../* @__PURE__ */ new Set([
3258
+ ...Object.values(partial).flatMap(
3259
+ (value) => value && typeof value === "object" && "sourceNodeIds" in value ? value.sourceNodeIds : []
3260
+ ),
3261
+ ...coverages.flatMap((coverage) => coverage.sourceNodeIds)
3262
+ ])];
3263
+ const sourceSpanIds = [.../* @__PURE__ */ new Set([
3264
+ ...Object.values(partial).flatMap(
3265
+ (value) => value && typeof value === "object" && "sourceSpanIds" in value ? value.sourceSpanIds : []
3266
+ ),
3267
+ ...coverages.flatMap((coverage) => coverage.sourceSpanIds)
3268
+ ])];
3269
+ return PolicyOperationalProfileSchema.parse({
3270
+ ...partial,
3271
+ coverageTypes,
3272
+ coverages,
3273
+ parties: buildParties(partial),
3274
+ endorsementSupport: buildEndorsementSupport(nodes),
3275
+ sourceNodeIds,
3276
+ sourceSpanIds,
3277
+ warnings: [
3278
+ ...coverages.length === 0 ? ["No source-backed coverage schedule rows were identified deterministically."] : [],
3279
+ ...!partial.policyNumber ? ["No source-backed policy number was identified deterministically."] : [],
3280
+ ...!partial.namedInsured ? ["No source-backed named insured was identified deterministically."] : []
3281
+ ]
3282
+ });
3283
+ }
3284
+ function mergeOperationalProfile(base, candidate, validNodeIds, validSpanIds) {
3285
+ const keepIds = (ids, valid) => Array.isArray(ids) ? ids.filter((id) => typeof id === "string" && valid.has(id)) : [];
3286
+ const mergeValue = (fallback, next) => {
3287
+ if (!next || typeof next !== "object" || Array.isArray(next)) return fallback;
3288
+ const record = next;
3289
+ const value = typeof record.value === "string" ? cleanValue(record.value) : void 0;
3290
+ if (!value) return fallback;
3291
+ const sourceNodeIds = keepIds(record.sourceNodeIds, validNodeIds);
3292
+ const sourceSpanIds = keepIds(record.sourceSpanIds, validSpanIds);
3293
+ if (sourceNodeIds.length === 0 && sourceSpanIds.length === 0) return fallback;
3294
+ return {
3295
+ value,
3296
+ normalizedValue: typeof record.normalizedValue === "string" ? record.normalizedValue : fallback?.normalizedValue,
3297
+ confidence: record.confidence === "high" || record.confidence === "low" || record.confidence === "medium" ? record.confidence : "medium",
3298
+ sourceNodeIds,
3299
+ sourceSpanIds
3300
+ };
3301
+ };
3302
+ const coverages = Array.isArray(candidate.coverages) ? candidate.coverages.map((coverage) => ({
3303
+ ...coverage,
3304
+ sourceNodeIds: keepIds(coverage.sourceNodeIds, validNodeIds),
3305
+ sourceSpanIds: keepIds(coverage.sourceSpanIds, validSpanIds)
3306
+ })).filter((coverage) => coverage.name && (coverage.sourceNodeIds.length > 0 || coverage.sourceSpanIds.length > 0)) : base.coverages;
3307
+ return PolicyOperationalProfileSchema.parse({
3308
+ ...base,
3309
+ documentType: candidate.documentType === "quote" ? "quote" : candidate.documentType === "policy" ? "policy" : base.documentType,
3310
+ policyTypes: Array.isArray(candidate.policyTypes) && candidate.policyTypes.length > 0 ? candidate.policyTypes : base.policyTypes,
3311
+ policyNumber: mergeValue(base.policyNumber, candidate.policyNumber),
3312
+ namedInsured: mergeValue(base.namedInsured, candidate.namedInsured),
3313
+ insurer: mergeValue(base.insurer, candidate.insurer),
3314
+ broker: mergeValue(base.broker, candidate.broker),
3315
+ effectiveDate: mergeValue(base.effectiveDate, candidate.effectiveDate),
3316
+ expirationDate: mergeValue(base.expirationDate, candidate.expirationDate),
3317
+ retroactiveDate: mergeValue(base.retroactiveDate, candidate.retroactiveDate),
3318
+ premium: mergeValue(base.premium, candidate.premium),
3319
+ coverageTypes: Array.isArray(candidate.coverageTypes) && candidate.coverageTypes.length > 0 ? candidate.coverageTypes : base.coverageTypes,
3320
+ coverages,
3321
+ parties: base.parties,
3322
+ endorsementSupport: base.endorsementSupport,
3323
+ sourceNodeIds: [.../* @__PURE__ */ new Set([...base.sourceNodeIds, ...keepIds(candidate.sourceNodeIds, validNodeIds)])],
3324
+ sourceSpanIds: [.../* @__PURE__ */ new Set([...base.sourceSpanIds, ...keepIds(candidate.sourceSpanIds, validSpanIds)])],
3325
+ warnings: base.warnings
3326
+ });
3327
+ }
3328
+
2700
3329
  // src/extraction/pdf.ts
2701
3330
  var import_pdf_lib = require("pdf-lib");
2702
3331
  function isFileIdRef(input) {
@@ -3078,15 +3707,15 @@ function normalizeItem(ref, item) {
3078
3707
  if (!text) return void 0;
3079
3708
  const table = getItemTable(item);
3080
3709
  const pages = (item.prov ?? []).map((prov) => getPageNumber(prov)).filter((page) => typeof page === "number" && page > 0);
3081
- const pageStart = pages.length ? Math.min(...pages) : void 0;
3082
- const pageEnd = pages.length ? Math.max(...pages) : pageStart;
3710
+ const pageStart2 = pages.length ? Math.min(...pages) : void 0;
3711
+ const pageEnd2 = pages.length ? Math.max(...pages) : pageStart2;
3083
3712
  const bboxes = (item.prov ?? []).map((prov) => toSourceSpanBBox(prov)).filter((bbox) => Boolean(bbox));
3084
3713
  return {
3085
3714
  ref,
3086
3715
  label: typeof item.label === "string" ? item.label : void 0,
3087
3716
  text,
3088
- pageStart,
3089
- pageEnd,
3717
+ pageStart: pageStart2,
3718
+ pageEnd: pageEnd2,
3090
3719
  bboxes: bboxes.length ? bboxes : void 0,
3091
3720
  table
3092
3721
  };
@@ -3098,7 +3727,7 @@ function buildSourceSpansForUnit(unit, index, options) {
3098
3727
  doclingRef: unit.ref,
3099
3728
  ...unit.label ? { doclingLabel: unit.label } : {}
3100
3729
  };
3101
- const tableId = unit.table ? `${unit.ref}:table` : void 0;
3730
+ const tableId2 = unit.table ? `${unit.ref}:table` : void 0;
3102
3731
  const tableSpan = withDoclingKind(buildSourceSpan(
3103
3732
  {
3104
3733
  documentId: options.documentId,
@@ -3108,7 +3737,7 @@ function buildSourceSpansForUnit(unit, index, options) {
3108
3737
  pageEnd: unit.pageEnd,
3109
3738
  sectionId: unit.label,
3110
3739
  sourceUnit: unit.table ? "table" : labelToSourceUnit(unit.label),
3111
- table: tableId ? { tableId } : void 0,
3740
+ table: tableId2 ? { tableId: tableId2 } : void 0,
3112
3741
  metadata: baseMetadata
3113
3742
  },
3114
3743
  index * 1e3
@@ -3132,7 +3761,7 @@ function buildSourceSpansForUnit(unit, index, options) {
3132
3761
  sourceUnit: "table_row",
3133
3762
  parentSpanId: tableSpan.id,
3134
3763
  table: {
3135
- tableId,
3764
+ tableId: tableId2,
3136
3765
  tableSpanId: tableSpan.id,
3137
3766
  rowIndex,
3138
3767
  isHeader
@@ -3140,7 +3769,7 @@ function buildSourceSpansForUnit(unit, index, options) {
3140
3769
  metadata: {
3141
3770
  ...baseMetadata,
3142
3771
  sourceUnit: "table_row",
3143
- tableId: tableId ?? "",
3772
+ tableId: tableId2 ?? "",
3144
3773
  tableSpanId: tableSpan.id,
3145
3774
  rowIndex: String(rowIndex),
3146
3775
  isHeader: String(isHeader)
@@ -3164,7 +3793,7 @@ function buildSourceSpansForUnit(unit, index, options) {
3164
3793
  sourceUnit: "table_cell",
3165
3794
  parentSpanId: rowSpan.id,
3166
3795
  table: {
3167
- tableId,
3796
+ tableId: tableId2,
3168
3797
  tableSpanId: tableSpan.id,
3169
3798
  rowSpanId: rowSpan.id,
3170
3799
  rowIndex,
@@ -3175,7 +3804,7 @@ function buildSourceSpansForUnit(unit, index, options) {
3175
3804
  metadata: {
3176
3805
  ...baseMetadata,
3177
3806
  sourceUnit: "table_cell",
3178
- tableId: tableId ?? "",
3807
+ tableId: tableId2 ?? "",
3179
3808
  tableSpanId: tableSpan.id,
3180
3809
  rowSpanId: rowSpan.id,
3181
3810
  rowIndex: String(rowIndex),
@@ -3330,7 +3959,7 @@ function buildSourceContext(spans, maxChars = 12e3) {
3330
3959
  const lines = [];
3331
3960
  let length = 0;
3332
3961
  for (const span of orderSourceSpansForContext(spans)) {
3333
- const unit = sourceUnit2(span);
3962
+ const unit = sourceUnit3(span);
3334
3963
  const table = span.table;
3335
3964
  const tableContext = [
3336
3965
  unit ? ` unit:${unit}` : "",
@@ -3354,11 +3983,11 @@ ${lines.join("\n\n")}
3354
3983
 
3355
3984
  Use sourceSpan IDs when grounding extracted contractual values.`;
3356
3985
  }
3357
- function sourceUnit2(span) {
3986
+ function sourceUnit3(span) {
3358
3987
  return span.sourceUnit ?? span.metadata?.sourceUnit;
3359
3988
  }
3360
3989
  function sourceContextRank(span) {
3361
- switch (sourceUnit2(span)) {
3990
+ switch (sourceUnit3(span)) {
3362
3991
  case "table_row":
3363
3992
  return 0;
3364
3993
  case "table":
@@ -3377,10 +4006,10 @@ function sourceContextRank(span) {
3377
4006
  }
3378
4007
  function orderSourceSpansForContext(spans) {
3379
4008
  const parentRows = new Set(
3380
- spans.filter((span) => sourceUnit2(span) === "table_row").map((span) => span.id)
4009
+ spans.filter((span) => sourceUnit3(span) === "table_row").map((span) => span.id)
3381
4010
  );
3382
4011
  const filtered = spans.filter((span) => {
3383
- if (sourceUnit2(span) !== "table_cell") return true;
4012
+ if (sourceUnit3(span) !== "table_cell") return true;
3384
4013
  const parent = span.parentSpanId ?? span.table?.rowSpanId ?? span.metadata?.rowSpanId;
3385
4014
  return !parent || !parentRows.has(parent);
3386
4015
  });
@@ -3774,7 +4403,7 @@ function stringValue2(record, ...keys) {
3774
4403
  function stringArray(value) {
3775
4404
  return Array.isArray(value) ? value.filter((item) => typeof item === "string" && item.length > 0) : [];
3776
4405
  }
3777
- function sourceUnit3(span) {
4406
+ function sourceUnit4(span) {
3778
4407
  return span.sourceUnit ?? span.metadata?.sourceUnit;
3779
4408
  }
3780
4409
  function spanPage(span) {
@@ -3784,7 +4413,7 @@ function spanPageEnd(span) {
3784
4413
  return span.pageEnd ?? span.location?.endPage ?? spanPage(span);
3785
4414
  }
3786
4415
  function sourceSpansForPage(sourceSpans, page) {
3787
- return sourceSpans.filter((span) => spanPage(span) === page && sourceUnit3(span) === "page").map((span) => span.id);
4416
+ return sourceSpans.filter((span) => spanPage(span) === page && sourceUnit4(span) === "page").map((span) => span.id);
3788
4417
  }
3789
4418
  function nodePageOverlaps(node, record) {
3790
4419
  const recordStart = numberValue(record, "pageNumber", "pageStart", "resolvedFromPage");
@@ -3836,8 +4465,8 @@ function attachDocumentNodeIds(document, nodes) {
3836
4465
  function buildNodeFromSection(section, index) {
3837
4466
  const title = stringValue2(section, "title", "name", "sectionRef") ?? `Section ${index + 1}`;
3838
4467
  const id = stringValue2(section, "documentNodeId", "recordId") ?? `section:${index}:${slugPart(title)}`;
3839
- const pageStart = numberValue(section, "pageStart", "pageNumber");
3840
- const pageEnd = numberValue(section, "pageEnd", "pageNumber") ?? pageStart;
4468
+ const pageStart2 = numberValue(section, "pageStart", "pageNumber");
4469
+ const pageEnd2 = numberValue(section, "pageEnd", "pageNumber") ?? pageStart2;
3841
4470
  const type = stringValue2(section, "type");
3842
4471
  const coverageType = stringValue2(section, "coverageType");
3843
4472
  const sourceSpanIds = stringArray(section.sourceSpanIds);
@@ -3852,8 +4481,8 @@ function buildNodeFromSection(section, index) {
3852
4481
  label: type,
3853
4482
  level: 2,
3854
4483
  sectionNumber: stringValue2(subsection, "sectionNumber"),
3855
- pageStart: numberValue(subsection, "pageNumber") ?? pageStart,
3856
- pageEnd: numberValue(subsection, "pageNumber") ?? pageStart,
4484
+ pageStart: numberValue(subsection, "pageNumber") ?? pageStart2,
4485
+ pageEnd: numberValue(subsection, "pageNumber") ?? pageStart2,
3857
4486
  excerpt: stringValue2(subsection, "excerpt"),
3858
4487
  content: stringValue2(subsection, "content"),
3859
4488
  sourceSpanIds: stringArray(subsection.sourceSpanIds),
@@ -3868,8 +4497,8 @@ function buildNodeFromSection(section, index) {
3868
4497
  label: type,
3869
4498
  level: 1,
3870
4499
  sectionNumber: stringValue2(section, "sectionNumber"),
3871
- pageStart,
3872
- pageEnd,
4500
+ pageStart: pageStart2,
4501
+ pageEnd: pageEnd2,
3873
4502
  formNumber: stringValue2(section, "formNumber"),
3874
4503
  formTitle: stringValue2(section, "formTitle"),
3875
4504
  excerpt: stringValue2(section, "excerpt"),
@@ -3882,17 +4511,17 @@ function buildNodeFromSection(section, index) {
3882
4511
  }
3883
4512
  function buildNodesFromSourceSpans(sourceSpans) {
3884
4513
  const candidates = sourceSpans.filter((span) => {
3885
- const unit = sourceUnit3(span);
4514
+ const unit = sourceUnit4(span);
3886
4515
  return unit === "section" || unit === "section_candidate" || unit === "page";
3887
4516
  });
3888
4517
  return candidates.sort((left, right) => (spanPage(left) ?? 0) - (spanPage(right) ?? 0) || left.id.localeCompare(right.id)).map((span, index) => {
3889
- const title = span.sectionId ?? span.formNumber ?? (sourceUnit3(span) === "page" && spanPage(span) ? `Page ${spanPage(span)}` : `Source unit ${index + 1}`);
4518
+ const title = span.sectionId ?? span.formNumber ?? (sourceUnit4(span) === "page" && spanPage(span) ? `Page ${spanPage(span)}` : `Source unit ${index + 1}`);
3890
4519
  return {
3891
4520
  id: `source:${index}:${slugPart(span.id)}`,
3892
4521
  title,
3893
4522
  originalTitle: title,
3894
- type: sourceUnit3(span),
3895
- label: sourceUnit3(span),
4523
+ type: sourceUnit4(span),
4524
+ label: sourceUnit4(span),
3896
4525
  level: 1,
3897
4526
  pageStart: spanPage(span),
3898
4527
  pageEnd: spanPageEnd(span),
@@ -7644,7 +8273,7 @@ function numberValue2(value) {
7644
8273
  function normalize(value) {
7645
8274
  return value.toLowerCase().replace(/&/g, "and").replace(/[^a-z0-9]+/g, " ").trim();
7646
8275
  }
7647
- function sourceUnit4(span) {
8276
+ function sourceUnit5(span) {
7648
8277
  return span.sourceUnit ?? span.metadata?.sourceUnit;
7649
8278
  }
7650
8279
  function pageNumber(span) {
@@ -7714,7 +8343,7 @@ function firstField(fields, patterns) {
7714
8343
  }
7715
8344
  function coverageFromRow(span) {
7716
8345
  const rowText = span.text.trim();
7717
- if (!rowText || sourceUnit4(span) !== "table_row") return void 0;
8346
+ if (!rowText || sourceUnit5(span) !== "table_row") return void 0;
7718
8347
  if (span.table?.isHeader || span.metadata?.isHeader === "true") return void 0;
7719
8348
  const fields = splitRowFields(rowText);
7720
8349
  let name;
@@ -7794,7 +8423,7 @@ function recoverCoverageScheduleRows(params) {
7794
8423
  const payload = params.memory.get("coverage_limits");
7795
8424
  const existing = Array.isArray(payload?.coverages) ? payload.coverages : [];
7796
8425
  const pages = coveragePages(params.pageAssignments);
7797
- const candidates = params.sourceSpans.filter((span) => sourceUnit4(span) === "table_row").filter((span) => {
8426
+ const candidates = params.sourceSpans.filter((span) => sourceUnit5(span) === "table_row").filter((span) => {
7798
8427
  const page = pageNumber(span);
7799
8428
  return page !== void 0 && pages.has(page);
7800
8429
  }).map(coverageFromRow).filter((coverage) => Boolean(coverage));
@@ -8430,13 +9059,13 @@ function buildPlanFromPageAssignments(pageAssignments, pageCount, formInventory)
8430
9059
  const expanded = new Set(pages);
8431
9060
  for (const page of pages) {
8432
9061
  for (const form of contextualForms) {
8433
- const pageStart = form.pageStart;
8434
- const pageEnd = form.pageEnd ?? form.pageStart;
9062
+ const pageStart2 = form.pageStart;
9063
+ const pageEnd2 = form.pageEnd ?? form.pageStart;
8435
9064
  const formType = form.formType;
8436
9065
  const supportsContextualExpansion = extractorName === "endorsements" ? formType === "endorsement" : formType === "coverage" || formType === "endorsement";
8437
9066
  if (!supportsContextualExpansion) continue;
8438
- if (page < pageStart || page > pageEnd) continue;
8439
- for (let current = pageStart; current <= pageEnd; current += 1) {
9067
+ if (page < pageStart2 || page > pageEnd2) continue;
9068
+ for (let current = pageStart2; current <= pageEnd2; current += 1) {
8440
9069
  expanded.add(current);
8441
9070
  }
8442
9071
  }
@@ -8517,11 +9146,11 @@ function textMatches(record, span) {
8517
9146
  function sourceHashFor(spans) {
8518
9147
  return spans.map((span) => span.textHash ?? span.hash).filter(Boolean).join(":") || void 0;
8519
9148
  }
8520
- function sourceUnit5(span) {
9149
+ function sourceUnit6(span) {
8521
9150
  return span.sourceUnit ?? span.metadata?.sourceUnit;
8522
9151
  }
8523
9152
  function hierarchyScore(span) {
8524
- switch (sourceUnit5(span)) {
9153
+ switch (sourceUnit6(span)) {
8525
9154
  case "table_row":
8526
9155
  return 5;
8527
9156
  case "table":
@@ -8544,8 +9173,8 @@ function preferParentRows(matches, sourceSpans) {
8544
9173
  const expanded = [];
8545
9174
  const seen = /* @__PURE__ */ new Set();
8546
9175
  for (const match of matches) {
8547
- const parent = sourceUnit5(match) === "table_cell" ? byId.get(parentRowId(match) ?? "") : void 0;
8548
- const preferred = parent && sourceUnit5(parent) === "table_row" ? parent : match;
9176
+ const parent = sourceUnit6(match) === "table_cell" ? byId.get(parentRowId(match) ?? "") : void 0;
9177
+ const preferred = parent && sourceUnit6(parent) === "table_row" ? parent : match;
8549
9178
  if (seen.has(preferred.id)) continue;
8550
9179
  seen.add(preferred.id);
8551
9180
  expanded.push(preferred);
@@ -8554,11 +9183,11 @@ function preferParentRows(matches, sourceSpans) {
8554
9183
  }
8555
9184
  function findSourceSpansForRecord(record, sourceSpans) {
8556
9185
  if (sourceSpans.length === 0) return [];
8557
- const pageStart = numberValue3(record, "pageNumber", "pageStart");
8558
- const pageEnd = numberValue3(record, "pageNumber", "pageEnd");
9186
+ const pageStart2 = numberValue3(record, "pageNumber", "pageStart");
9187
+ const pageEnd2 = numberValue3(record, "pageNumber", "pageEnd");
8559
9188
  const scored = sourceSpans.map((span) => {
8560
9189
  let score = 0;
8561
- if (pageOverlaps(pageStart, pageEnd, span)) score += 4;
9190
+ if (pageOverlaps(pageStart2, pageEnd2, span)) score += 4;
8562
9191
  if (formMatches(record, span)) score += 3;
8563
9192
  if (textMatches(record, span)) score += 2;
8564
9193
  if (score > 0) score += hierarchyScore(span);
@@ -8597,6 +9226,424 @@ function groundExtractionMemoryWithSourceSpans(memory, sourceSpans) {
8597
9226
  }
8598
9227
  }
8599
9228
 
9229
+ // src/extraction/source-tree-extractor.ts
9230
+ var import_zod41 = require("zod");
9231
+ var ORGANIZABLE_KINDS = [
9232
+ "page_group",
9233
+ "form",
9234
+ "endorsement",
9235
+ "section",
9236
+ "schedule",
9237
+ "clause"
9238
+ ];
9239
+ var SourceTreeOrganizationSchema = import_zod41.z.object({
9240
+ labels: import_zod41.z.array(import_zod41.z.object({
9241
+ nodeId: import_zod41.z.string(),
9242
+ kind: import_zod41.z.enum([
9243
+ "document",
9244
+ "page_group",
9245
+ "page",
9246
+ "form",
9247
+ "endorsement",
9248
+ "section",
9249
+ "schedule",
9250
+ "clause",
9251
+ "table",
9252
+ "table_row",
9253
+ "table_cell",
9254
+ "text"
9255
+ ]).optional(),
9256
+ title: import_zod41.z.string().optional(),
9257
+ description: import_zod41.z.string().optional()
9258
+ })),
9259
+ groups: import_zod41.z.array(import_zod41.z.object({
9260
+ kind: import_zod41.z.enum(ORGANIZABLE_KINDS),
9261
+ title: import_zod41.z.string(),
9262
+ description: import_zod41.z.string().optional(),
9263
+ childNodeIds: import_zod41.z.array(import_zod41.z.string()).min(1)
9264
+ }))
9265
+ });
9266
+ var SourceBackedValueForPromptSchema = import_zod41.z.object({
9267
+ value: import_zod41.z.string(),
9268
+ normalizedValue: import_zod41.z.string().optional(),
9269
+ confidence: import_zod41.z.enum(["low", "medium", "high"]).optional(),
9270
+ sourceNodeIds: import_zod41.z.array(import_zod41.z.string()),
9271
+ sourceSpanIds: import_zod41.z.array(import_zod41.z.string())
9272
+ });
9273
+ var OperationalProfilePromptSchema = import_zod41.z.object({
9274
+ documentType: import_zod41.z.enum(["policy", "quote"]).optional(),
9275
+ policyTypes: import_zod41.z.array(import_zod41.z.string()).optional(),
9276
+ policyNumber: SourceBackedValueForPromptSchema.optional(),
9277
+ namedInsured: SourceBackedValueForPromptSchema.optional(),
9278
+ insurer: SourceBackedValueForPromptSchema.optional(),
9279
+ broker: SourceBackedValueForPromptSchema.optional(),
9280
+ effectiveDate: SourceBackedValueForPromptSchema.optional(),
9281
+ expirationDate: SourceBackedValueForPromptSchema.optional(),
9282
+ retroactiveDate: SourceBackedValueForPromptSchema.optional(),
9283
+ premium: SourceBackedValueForPromptSchema.optional(),
9284
+ coverageTypes: import_zod41.z.array(import_zod41.z.string()).optional(),
9285
+ coverages: import_zod41.z.array(import_zod41.z.object({
9286
+ name: import_zod41.z.string(),
9287
+ coverageCode: import_zod41.z.string().optional(),
9288
+ limit: import_zod41.z.string().optional(),
9289
+ deductible: import_zod41.z.string().optional(),
9290
+ premium: import_zod41.z.string().optional(),
9291
+ formNumber: import_zod41.z.string().optional(),
9292
+ sectionRef: import_zod41.z.string().optional(),
9293
+ sourceNodeIds: import_zod41.z.array(import_zod41.z.string()),
9294
+ sourceSpanIds: import_zod41.z.array(import_zod41.z.string())
9295
+ })).optional(),
9296
+ sourceNodeIds: import_zod41.z.array(import_zod41.z.string()).optional(),
9297
+ sourceSpanIds: import_zod41.z.array(import_zod41.z.string()).optional()
9298
+ });
9299
+ function cleanText(value, fallback) {
9300
+ const text = value?.replace(/\s+/g, " ").trim();
9301
+ return text || fallback;
9302
+ }
9303
+ function compactNode(node) {
9304
+ return {
9305
+ id: node.id,
9306
+ kind: node.kind,
9307
+ title: node.title,
9308
+ path: node.path,
9309
+ pageStart: node.pageStart,
9310
+ pageEnd: node.pageEnd,
9311
+ sourceSpanIds: node.sourceSpanIds.slice(0, 8),
9312
+ text: (node.textExcerpt ?? node.description).slice(0, 700)
9313
+ };
9314
+ }
9315
+ function buildOrganizationPrompt(sourceTree) {
9316
+ const nodes = sourceTree.filter((node) => node.kind !== "document").slice(0, 240).map(compactNode);
9317
+ return `You organize an insurance document source tree.
9318
+
9319
+ Rules:
9320
+ - Use only node IDs from the provided list.
9321
+ - Do not invent text, page numbers, source spans, limits, or policy facts.
9322
+ - You may relabel existing nodes and group adjacent top-level/page nodes when they are clearly one form, endorsement, declarations set, schedule, or clause family.
9323
+ - Add concise, human-readable titles to generic text, table, row, and cell nodes when the text makes their role clear.
9324
+ - Groups must list existing childNodeIds only.
9325
+ - Keep descriptions short and useful for search.
9326
+
9327
+ Source nodes:
9328
+ ${JSON.stringify(nodes, null, 2)}
9329
+
9330
+ Return JSON with labels and groups only.`;
9331
+ }
9332
+ function buildOperationalProfilePrompt(sourceTree, fallback) {
9333
+ const nodes = sourceTree.filter((node) => node.kind !== "document").slice(0, 240).map(compactNode);
9334
+ return `Extract a source-backed operational profile for an insurance policy or quote.
9335
+
9336
+ Return only high-value operational facts needed for policy lists, Q&A, compliance, and certificate generation:
9337
+ - policy number, named insured, insurer/carrier/security, broker/producer, policy period, retroactive date, premium
9338
+ - coverage lines with limits, deductibles, premiums, and form references
9339
+ - coverage type labels
9340
+
9341
+ Rules:
9342
+ - Every returned value must include sourceNodeIds or sourceSpanIds from the provided nodes.
9343
+ - If a value is not directly supported, omit it.
9344
+ - Prefer declarations, schedules, premium tables, and endorsement schedules over generic policy wording.
9345
+ - Do not copy entire policy wording into fields.
9346
+
9347
+ Deterministic baseline:
9348
+ ${JSON.stringify(fallback, null, 2)}
9349
+
9350
+ Source nodes:
9351
+ ${JSON.stringify(nodes, null, 2)}
9352
+
9353
+ Return JSON for the operational profile.`;
9354
+ }
9355
+ function groupNodeId(documentId, group) {
9356
+ return [
9357
+ documentId.replace(/[^a-zA-Z0-9_.:-]/g, "_"),
9358
+ "source_node",
9359
+ group.kind,
9360
+ group.childNodeIds.join("_").replace(/[^a-zA-Z0-9_.:-]/g, "_").slice(0, 80)
9361
+ ].join(":");
9362
+ }
9363
+ function applyOrganization(sourceTree, organization) {
9364
+ const byId = new Map(sourceTree.map((node) => [node.id, node]));
9365
+ let nextTree = sourceTree.map((node) => {
9366
+ const label = organization.labels.find((item) => item.nodeId === node.id);
9367
+ if (!label) return node;
9368
+ return {
9369
+ ...node,
9370
+ kind: label.kind ?? node.kind,
9371
+ title: cleanText(label.title, node.title),
9372
+ description: cleanText(label.description, node.description)
9373
+ };
9374
+ });
9375
+ for (const group of organization.groups.slice(0, 40)) {
9376
+ const children = group.childNodeIds.map((id2) => byId.get(id2)).filter((node2) => Boolean(node2));
9377
+ if (children.length === 0) continue;
9378
+ const parentId = children[0].parentId;
9379
+ if (!children.every((child) => child.parentId === parentId)) continue;
9380
+ const documentId = children[0].documentId;
9381
+ const id = groupNodeId(documentId, group);
9382
+ if (byId.has(id)) continue;
9383
+ const sourceSpanIds = [...new Set(children.flatMap((child) => child.sourceSpanIds))];
9384
+ const pageStarts = children.map((child) => child.pageStart).filter((page) => typeof page === "number");
9385
+ const pageEnds = children.map((child) => child.pageEnd ?? child.pageStart).filter((page) => typeof page === "number");
9386
+ const order = Math.min(...children.map((child) => child.order));
9387
+ const node = {
9388
+ id,
9389
+ documentId,
9390
+ parentId,
9391
+ kind: group.kind,
9392
+ title: group.title,
9393
+ description: group.description ?? group.title,
9394
+ textExcerpt: children.map((child) => child.textExcerpt ?? child.description).filter(Boolean).join("\n\n").slice(0, 1600),
9395
+ sourceSpanIds,
9396
+ pageStart: pageStarts.length ? Math.min(...pageStarts) : void 0,
9397
+ pageEnd: pageEnds.length ? Math.max(...pageEnds) : void 0,
9398
+ bbox: children.flatMap((child) => child.bbox ?? []).slice(0, 12),
9399
+ order,
9400
+ path: "",
9401
+ metadata: { sourceTreeVersion: "v3", organizer: "llm_group" }
9402
+ };
9403
+ nextTree = [
9404
+ ...nextTree.map(
9405
+ (child) => group.childNodeIds.includes(child.id) ? { ...child, parentId: id, order: child.order + 1e-3 } : child
9406
+ ),
9407
+ node
9408
+ ];
9409
+ byId.set(id, node);
9410
+ }
9411
+ return normalizeDocumentSourceTreePaths(nextTree);
9412
+ }
9413
+ function sourceTreeToOutline(sourceTree) {
9414
+ const byParent = /* @__PURE__ */ new Map();
9415
+ for (const node of sourceTree.filter((item) => item.kind !== "document")) {
9416
+ const group = byParent.get(node.parentId) ?? [];
9417
+ group.push(node);
9418
+ byParent.set(node.parentId, group);
9419
+ }
9420
+ const root = sourceTree.find((node) => node.kind === "document");
9421
+ const visit = (node) => ({
9422
+ id: node.id,
9423
+ title: node.title,
9424
+ type: node.kind,
9425
+ label: node.kind,
9426
+ pageStart: node.pageStart,
9427
+ pageEnd: node.pageEnd,
9428
+ excerpt: node.textExcerpt,
9429
+ content: node.textExcerpt,
9430
+ sourceSpanIds: node.sourceSpanIds,
9431
+ sourceTextHash: node.sourceSpanIds.join(":") || void 0,
9432
+ interpretationLabels: [node.kind],
9433
+ metadata: node.metadata,
9434
+ children: (byParent.get(node.id) ?? []).map(visit)
9435
+ });
9436
+ return (byParent.get(root?.id) ?? []).map(visit);
9437
+ }
9438
+ function valueOf(profile, key) {
9439
+ const value = profile[key];
9440
+ return value && typeof value === "object" && !Array.isArray(value) && "value" in value ? String(value.value) : void 0;
9441
+ }
9442
+ function materializeDocument(params) {
9443
+ const profile = params.operationalProfile;
9444
+ const policyNumber = valueOf(profile, "policyNumber") ?? "Unknown";
9445
+ const insuredName = valueOf(profile, "namedInsured") ?? "Unknown";
9446
+ const carrier = valueOf(profile, "insurer") ?? "Unknown";
9447
+ const effectiveDate = valueOf(profile, "effectiveDate") ?? "Unknown";
9448
+ const expirationDate = valueOf(profile, "expirationDate") ?? "Unknown";
9449
+ const premium = valueOf(profile, "premium");
9450
+ const coverages = profile.coverages.map((coverage) => ({
9451
+ name: coverage.name,
9452
+ coverageCode: coverage.coverageCode,
9453
+ limit: coverage.limit,
9454
+ deductible: coverage.deductible,
9455
+ premium: coverage.premium,
9456
+ formNumber: coverage.formNumber,
9457
+ sectionRef: coverage.sectionRef,
9458
+ sourceSpanIds: coverage.sourceSpanIds,
9459
+ documentNodeId: coverage.sourceNodeIds[0],
9460
+ originalContent: [coverage.name, coverage.limit, coverage.deductible, coverage.premium].filter(Boolean).join(" | ")
9461
+ }));
9462
+ const documentOutline = sourceTreeToOutline(params.sourceTree);
9463
+ const documentMetadata = {
9464
+ sourceTreeVersion: "v3",
9465
+ sourceTreeCanonical: true,
9466
+ tableOfContents: documentOutline.map((node) => ({
9467
+ title: node.title,
9468
+ pageStart: node.pageStart,
9469
+ pageEnd: node.pageEnd,
9470
+ documentNodeId: node.id,
9471
+ sourceSpanIds: node.sourceSpanIds
9472
+ })),
9473
+ agentGuidance: [
9474
+ {
9475
+ kind: "source_tree",
9476
+ title: "Use the source tree as canonical evidence",
9477
+ detail: "Operational fields are projections from source nodes and source spans. Use source nodes for policy wording and exact provenance."
9478
+ }
9479
+ ]
9480
+ };
9481
+ const summary = [
9482
+ carrier !== "Unknown" ? carrier : void 0,
9483
+ policyNumber !== "Unknown" ? `#${policyNumber}` : void 0,
9484
+ insuredName !== "Unknown" ? `for ${insuredName}` : void 0,
9485
+ profile.coverageTypes.length ? `covering ${profile.coverageTypes.slice(0, 5).join(", ")}` : void 0
9486
+ ].filter(Boolean).join(" ");
9487
+ const base = {
9488
+ id: params.id,
9489
+ type: profile.documentType,
9490
+ carrier,
9491
+ security: carrier,
9492
+ insuredName,
9493
+ premium,
9494
+ policyTypes: profile.policyTypes,
9495
+ coverages,
9496
+ documentMetadata,
9497
+ documentOutline,
9498
+ declarations: {
9499
+ fields: [
9500
+ profile.policyNumber ? { field: "policyNumber", value: profile.policyNumber.value, sourceSpanIds: profile.policyNumber.sourceSpanIds } : void 0,
9501
+ profile.namedInsured ? { field: "namedInsured", value: profile.namedInsured.value, sourceSpanIds: profile.namedInsured.sourceSpanIds } : void 0,
9502
+ profile.insurer ? { field: "insurer", value: profile.insurer.value, sourceSpanIds: profile.insurer.sourceSpanIds } : void 0,
9503
+ profile.effectiveDate ? { field: "policyPeriodStart", value: profile.effectiveDate.value, sourceSpanIds: profile.effectiveDate.sourceSpanIds } : void 0,
9504
+ profile.expirationDate ? { field: "policyPeriodEnd", value: profile.expirationDate.value, sourceSpanIds: profile.expirationDate.sourceSpanIds } : void 0
9505
+ ].filter(Boolean)
9506
+ },
9507
+ supplementaryFacts: profile.endorsementSupport.map((item) => ({
9508
+ key: item.kind,
9509
+ value: item.summary,
9510
+ sourceSpanIds: item.sourceSpanIds,
9511
+ documentNodeId: item.sourceNodeIds[0]
9512
+ })),
9513
+ summary: summary || void 0
9514
+ };
9515
+ if (profile.documentType === "quote") {
9516
+ return {
9517
+ ...base,
9518
+ type: "quote",
9519
+ quoteNumber: policyNumber,
9520
+ proposedEffectiveDate: effectiveDate === "Unknown" ? void 0 : effectiveDate,
9521
+ proposedExpirationDate: expirationDate === "Unknown" ? void 0 : expirationDate
9522
+ };
9523
+ }
9524
+ return {
9525
+ ...base,
9526
+ type: "policy",
9527
+ policyNumber,
9528
+ effectiveDate,
9529
+ expirationDate,
9530
+ retroactiveDate: valueOf(profile, "retroactiveDate")
9531
+ };
9532
+ }
9533
+ async function runSourceTreeExtraction(params) {
9534
+ let sourceTree = buildDocumentSourceTree(params.sourceSpans, params.id);
9535
+ const warnings = [];
9536
+ let modelCalls = 0;
9537
+ let callsWithUsage = 0;
9538
+ let callsMissingUsage = 0;
9539
+ const tokenUsage = { inputTokens: 0, outputTokens: 0 };
9540
+ const performanceReport = { modelCalls: [], totalModelCallDurationMs: 0 };
9541
+ const localTrack = (usage, report) => {
9542
+ modelCalls += 1;
9543
+ if (usage) {
9544
+ callsWithUsage += 1;
9545
+ tokenUsage.inputTokens += usage.inputTokens;
9546
+ tokenUsage.outputTokens += usage.outputTokens;
9547
+ } else {
9548
+ callsMissingUsage += 1;
9549
+ }
9550
+ if (report) {
9551
+ performanceReport.modelCalls.push({ ...report, usage, usageReported: !!usage });
9552
+ if (report.durationMs != null) performanceReport.totalModelCallDurationMs += report.durationMs;
9553
+ }
9554
+ params.trackUsage(usage, report);
9555
+ };
9556
+ try {
9557
+ const budget = params.resolveBudget("extraction_source_tree", 4096);
9558
+ const startedAt = Date.now();
9559
+ const response = await safeGenerateObject(
9560
+ params.generateObject,
9561
+ {
9562
+ prompt: buildOrganizationPrompt(sourceTree),
9563
+ schema: SourceTreeOrganizationSchema,
9564
+ maxTokens: budget.maxTokens,
9565
+ taskKind: "extraction_source_tree",
9566
+ budgetDiagnostics: budget,
9567
+ providerOptions: { ...params.providerOptions, sourceSpans: params.sourceSpans }
9568
+ },
9569
+ {
9570
+ fallback: { labels: [], groups: [] },
9571
+ log: params.log
9572
+ }
9573
+ );
9574
+ localTrack(response.usage, {
9575
+ taskKind: "extraction_source_tree",
9576
+ label: "source_tree_organizer",
9577
+ maxTokens: budget.maxTokens,
9578
+ durationMs: Date.now() - startedAt
9579
+ });
9580
+ sourceTree = applyOrganization(sourceTree, response.object);
9581
+ } catch (error) {
9582
+ warnings.push(`Source-tree organizer failed; deterministic tree used (${error instanceof Error ? error.message : String(error)})`);
9583
+ }
9584
+ const deterministicProfile = buildDeterministicOperationalProfile({
9585
+ sourceTree,
9586
+ sourceSpans: params.sourceSpans
9587
+ });
9588
+ let operationalProfile = deterministicProfile;
9589
+ try {
9590
+ const validNodeIds = new Set(sourceTree.map((node) => node.id));
9591
+ const validSpanIds = new Set(params.sourceSpans.map((span) => span.id));
9592
+ const budget = params.resolveBudget("extraction_operational_profile", 8192);
9593
+ const startedAt = Date.now();
9594
+ const response = await safeGenerateObject(
9595
+ params.generateObject,
9596
+ {
9597
+ prompt: buildOperationalProfilePrompt(sourceTree, deterministicProfile),
9598
+ schema: OperationalProfilePromptSchema,
9599
+ maxTokens: budget.maxTokens,
9600
+ taskKind: "extraction_operational_profile",
9601
+ budgetDiagnostics: budget,
9602
+ providerOptions: { ...params.providerOptions, sourceSpans: params.sourceSpans, sourceTree }
9603
+ },
9604
+ {
9605
+ fallback: deterministicProfile,
9606
+ log: params.log
9607
+ }
9608
+ );
9609
+ localTrack(response.usage, {
9610
+ taskKind: "extraction_operational_profile",
9611
+ label: "operational_profile",
9612
+ maxTokens: budget.maxTokens,
9613
+ durationMs: Date.now() - startedAt
9614
+ });
9615
+ operationalProfile = mergeOperationalProfile(
9616
+ deterministicProfile,
9617
+ response.object,
9618
+ validNodeIds,
9619
+ validSpanIds
9620
+ );
9621
+ } catch (error) {
9622
+ warnings.push(`Operational profile model pass failed; deterministic profile used (${error instanceof Error ? error.message : String(error)})`);
9623
+ }
9624
+ const document = materializeDocument({
9625
+ id: params.id,
9626
+ sourceTree,
9627
+ operationalProfile
9628
+ });
9629
+ return {
9630
+ sourceTree,
9631
+ sourceSpans: params.sourceSpans,
9632
+ sourceChunks: chunkSourceSpans(params.sourceSpans),
9633
+ operationalProfile,
9634
+ document,
9635
+ chunks: [],
9636
+ warnings: [...warnings, ...operationalProfile.warnings],
9637
+ tokenUsage,
9638
+ usageReporting: {
9639
+ modelCalls,
9640
+ callsWithUsage,
9641
+ callsMissingUsage
9642
+ },
9643
+ performanceReport
9644
+ };
9645
+ }
9646
+
8600
9647
  // src/extraction/coordinator.ts
8601
9648
  function createExtractor(config) {
8602
9649
  const {
@@ -8782,17 +9829,17 @@ ${span.text}` : span.text;
8782
9829
  const candidateSpans = spansForPageRange(spans, startPage, endPage).filter((span) => span.text.trim().length > 0).filter((span) => span.metadata?.sourceUnit === "section_candidate" || span.sectionId || span.text.length >= 160);
8783
9830
  return {
8784
9831
  sections: candidateSpans.map((span, index) => {
8785
- const pageStart = span.pageStart ?? span.location?.startPage ?? span.location?.page ?? startPage;
8786
- const pageEnd = span.pageEnd ?? span.location?.endPage ?? pageStart;
8787
- const title = span.sectionId ?? span.formNumber ?? firstHeadingLine(span.text) ?? `Policy text page ${pageStart}`;
9832
+ const pageStart2 = span.pageStart ?? span.location?.startPage ?? span.location?.page ?? startPage;
9833
+ const pageEnd2 = span.pageEnd ?? span.location?.endPage ?? pageStart2;
9834
+ const title = span.sectionId ?? span.formNumber ?? firstHeadingLine(span.text) ?? `Policy text page ${pageStart2}`;
8788
9835
  return {
8789
9836
  title,
8790
9837
  sectionNumber: span.formNumber,
8791
- pageStart,
8792
- pageEnd,
9838
+ pageStart: pageStart2,
9839
+ pageEnd: pageEnd2,
8793
9840
  type: inferSectionType(title, span.text),
8794
9841
  excerpt: span.text.slice(0, 240),
8795
- recordId: `section_index_${pageStart}_${index}`,
9842
+ recordId: `section_index_${pageStart2}_${index}`,
8796
9843
  sourceSpanIds: [span.id],
8797
9844
  sourceTextHash: span.textHash ?? sourceSpanTextHash(span.text)
8798
9845
  };
@@ -8931,6 +9978,51 @@ ${span.text}` : span.text;
8931
9978
  await sourceStore.addSourceChunks(sourceChunks);
8932
9979
  }
8933
9980
  }
9981
+ if (sourceSpans.length > 0) {
9982
+ onProgress?.("Building source-native document tree...");
9983
+ const v3 = await runSourceTreeExtraction({
9984
+ id,
9985
+ sourceSpans,
9986
+ generateObject,
9987
+ providerOptions: activeProviderOptions,
9988
+ resolveBudget,
9989
+ trackUsage,
9990
+ log
9991
+ });
9992
+ const reviewReport2 = {
9993
+ issues: v3.warnings.map((warning) => ({
9994
+ code: "source_tree_warning",
9995
+ severity: "warning",
9996
+ message: warning
9997
+ })),
9998
+ rounds: [],
9999
+ artifacts: [
10000
+ { kind: "source_tree", label: "Source Tree", itemCount: v3.sourceTree.length },
10001
+ { kind: "source_spans", label: "Source Spans", itemCount: v3.sourceSpans.length },
10002
+ { kind: "operational_profile", label: "Operational Profile", itemCount: v3.operationalProfile.coverages.length }
10003
+ ],
10004
+ reviewRoundRecords: [],
10005
+ formInventory: [],
10006
+ qualityGateStatus: v3.warnings.length > 0 ? "warning" : "passed"
10007
+ };
10008
+ if (shouldFailQualityGate(qualityGate, reviewReport2.qualityGateStatus)) {
10009
+ throw new Error("Extraction quality gate failed. See reviewReport for blocking issues.");
10010
+ }
10011
+ onProgress?.("Source-tree extraction complete.");
10012
+ return {
10013
+ document: v3.document,
10014
+ chunks: [],
10015
+ sourceSpans: v3.sourceSpans,
10016
+ sourceChunks: v3.sourceChunks,
10017
+ sourceTree: v3.sourceTree,
10018
+ operationalProfile: v3.operationalProfile,
10019
+ warnings: v3.warnings,
10020
+ tokenUsage: v3.tokenUsage,
10021
+ usageReporting: v3.usageReporting,
10022
+ performanceReport: v3.performanceReport,
10023
+ reviewReport: reviewReport2
10024
+ };
10025
+ }
8934
10026
  const pipelineCtx = createPipelineContext({
8935
10027
  id,
8936
10028
  onSave: onCheckpointSave,
@@ -9801,8 +10893,8 @@ Respond with JSON only:
9801
10893
  }`;
9802
10894
 
9803
10895
  // src/schemas/application.ts
9804
- var import_zod41 = require("zod");
9805
- var FieldTypeSchema = import_zod41.z.enum([
10896
+ var import_zod42 = require("zod");
10897
+ var FieldTypeSchema = import_zod42.z.enum([
9806
10898
  "text",
9807
10899
  "numeric",
9808
10900
  "currency",
@@ -9811,138 +10903,138 @@ var FieldTypeSchema = import_zod41.z.enum([
9811
10903
  "table",
9812
10904
  "declaration"
9813
10905
  ]);
9814
- var ApplicationFieldSchema = import_zod41.z.object({
9815
- id: import_zod41.z.string(),
9816
- label: import_zod41.z.string(),
9817
- section: import_zod41.z.string(),
10906
+ var ApplicationFieldSchema = import_zod42.z.object({
10907
+ id: import_zod42.z.string(),
10908
+ label: import_zod42.z.string(),
10909
+ section: import_zod42.z.string(),
9818
10910
  fieldType: FieldTypeSchema,
9819
- required: import_zod41.z.boolean(),
9820
- options: import_zod41.z.array(import_zod41.z.string()).optional(),
9821
- columns: import_zod41.z.array(import_zod41.z.string()).optional(),
9822
- requiresExplanationIfYes: import_zod41.z.boolean().optional(),
9823
- condition: import_zod41.z.object({
9824
- dependsOn: import_zod41.z.string(),
9825
- whenValue: import_zod41.z.string()
10911
+ required: import_zod42.z.boolean(),
10912
+ options: import_zod42.z.array(import_zod42.z.string()).optional(),
10913
+ columns: import_zod42.z.array(import_zod42.z.string()).optional(),
10914
+ requiresExplanationIfYes: import_zod42.z.boolean().optional(),
10915
+ condition: import_zod42.z.object({
10916
+ dependsOn: import_zod42.z.string(),
10917
+ whenValue: import_zod42.z.string()
9826
10918
  }).optional(),
9827
- value: import_zod41.z.string().optional(),
9828
- source: import_zod41.z.string().optional().describe("Where the value came from: auto-fill, user, lookup"),
9829
- confidence: import_zod41.z.enum(["confirmed", "high", "medium", "low"]).optional(),
9830
- sourceSpanIds: import_zod41.z.array(import_zod41.z.string()).optional().describe("Stable source spans that support the field value or field anchor"),
9831
- userSourceSpanIds: import_zod41.z.array(import_zod41.z.string()).optional().describe("Message or attachment spans that support user-provided values"),
9832
- pageNumber: import_zod41.z.number().int().positive().optional().describe("Application page where the field label or anchor appears"),
9833
- fieldAnchorId: import_zod41.z.string().optional().describe("Stable field anchor ID derived from page, section, label, and form metadata"),
9834
- acroFormName: import_zod41.z.string().optional().describe("Native PDF AcroForm field name when available"),
9835
- validationStatus: import_zod41.z.enum(["valid", "needs_review", "unsupported", "missing"]).optional()
9836
- });
9837
- var ApplicationClassifyResultSchema = import_zod41.z.object({
9838
- isApplication: import_zod41.z.boolean(),
9839
- confidence: import_zod41.z.number().min(0).max(1),
9840
- applicationType: import_zod41.z.string().nullable()
9841
- });
9842
- var FieldExtractionResultSchema = import_zod41.z.object({
9843
- fields: import_zod41.z.array(ApplicationFieldSchema)
9844
- });
9845
- var AutoFillMatchSchema = import_zod41.z.object({
9846
- fieldId: import_zod41.z.string(),
9847
- value: import_zod41.z.string(),
9848
- confidence: import_zod41.z.enum(["confirmed"]),
9849
- contextKey: import_zod41.z.string()
9850
- });
9851
- var AutoFillResultSchema = import_zod41.z.object({
9852
- matches: import_zod41.z.array(AutoFillMatchSchema)
9853
- });
9854
- var QuestionBatchResultSchema = import_zod41.z.object({
9855
- batches: import_zod41.z.array(import_zod41.z.array(import_zod41.z.string()).describe("Array of field IDs in this batch"))
9856
- });
9857
- var LookupRequestSchema = import_zod41.z.object({
9858
- type: import_zod41.z.string().describe("Type of lookup: 'records', 'website', 'policy'"),
9859
- description: import_zod41.z.string(),
9860
- url: import_zod41.z.string().optional(),
9861
- targetFieldIds: import_zod41.z.array(import_zod41.z.string())
9862
- });
9863
- var ReplyIntentSchema = import_zod41.z.object({
9864
- primaryIntent: import_zod41.z.enum(["answers_only", "question", "lookup_request", "mixed"]),
9865
- hasAnswers: import_zod41.z.boolean(),
9866
- questionText: import_zod41.z.string().optional(),
9867
- questionFieldIds: import_zod41.z.array(import_zod41.z.string()).optional(),
9868
- lookupRequests: import_zod41.z.array(LookupRequestSchema).optional()
9869
- });
9870
- var ParsedAnswerSchema = import_zod41.z.object({
9871
- fieldId: import_zod41.z.string(),
9872
- value: import_zod41.z.string(),
9873
- explanation: import_zod41.z.string().optional()
9874
- });
9875
- var AnswerParsingResultSchema = import_zod41.z.object({
9876
- answers: import_zod41.z.array(ParsedAnswerSchema),
9877
- unanswered: import_zod41.z.array(import_zod41.z.string()).describe("Field IDs that were not answered")
9878
- });
9879
- var LookupFillSchema = import_zod41.z.object({
9880
- fieldId: import_zod41.z.string(),
9881
- value: import_zod41.z.string(),
9882
- source: import_zod41.z.string().describe("Specific citable reference, e.g. 'GL Policy #POL-12345 (Hartford)'"),
9883
- sourceSpanIds: import_zod41.z.array(import_zod41.z.string()).optional()
9884
- });
9885
- var LookupFillResultSchema = import_zod41.z.object({
9886
- fills: import_zod41.z.array(LookupFillSchema),
9887
- unfillable: import_zod41.z.array(import_zod41.z.string()),
9888
- explanation: import_zod41.z.string().optional()
9889
- });
9890
- var FlatPdfPlacementSchema = import_zod41.z.object({
9891
- fieldId: import_zod41.z.string(),
9892
- page: import_zod41.z.number(),
9893
- x: import_zod41.z.number().describe("Percentage from left edge (0-100)"),
9894
- y: import_zod41.z.number().describe("Percentage from top edge (0-100)"),
9895
- text: import_zod41.z.string(),
9896
- fontSize: import_zod41.z.number().optional(),
9897
- isCheckmark: import_zod41.z.boolean().optional()
9898
- });
9899
- var AcroFormMappingSchema = import_zod41.z.object({
9900
- fieldId: import_zod41.z.string(),
9901
- acroFormName: import_zod41.z.string(),
9902
- value: import_zod41.z.string()
9903
- });
9904
- var QualityGateStatusSchema = import_zod41.z.enum(["passed", "warning", "failed"]);
9905
- var QualitySeveritySchema = import_zod41.z.enum(["info", "warning", "blocking"]);
9906
- var ApplicationQualityIssueSchema = import_zod41.z.object({
9907
- code: import_zod41.z.string(),
10919
+ value: import_zod42.z.string().optional(),
10920
+ source: import_zod42.z.string().optional().describe("Where the value came from: auto-fill, user, lookup"),
10921
+ confidence: import_zod42.z.enum(["confirmed", "high", "medium", "low"]).optional(),
10922
+ sourceSpanIds: import_zod42.z.array(import_zod42.z.string()).optional().describe("Stable source spans that support the field value or field anchor"),
10923
+ userSourceSpanIds: import_zod42.z.array(import_zod42.z.string()).optional().describe("Message or attachment spans that support user-provided values"),
10924
+ pageNumber: import_zod42.z.number().int().positive().optional().describe("Application page where the field label or anchor appears"),
10925
+ fieldAnchorId: import_zod42.z.string().optional().describe("Stable field anchor ID derived from page, section, label, and form metadata"),
10926
+ acroFormName: import_zod42.z.string().optional().describe("Native PDF AcroForm field name when available"),
10927
+ validationStatus: import_zod42.z.enum(["valid", "needs_review", "unsupported", "missing"]).optional()
10928
+ });
10929
+ var ApplicationClassifyResultSchema = import_zod42.z.object({
10930
+ isApplication: import_zod42.z.boolean(),
10931
+ confidence: import_zod42.z.number().min(0).max(1),
10932
+ applicationType: import_zod42.z.string().nullable()
10933
+ });
10934
+ var FieldExtractionResultSchema = import_zod42.z.object({
10935
+ fields: import_zod42.z.array(ApplicationFieldSchema)
10936
+ });
10937
+ var AutoFillMatchSchema = import_zod42.z.object({
10938
+ fieldId: import_zod42.z.string(),
10939
+ value: import_zod42.z.string(),
10940
+ confidence: import_zod42.z.enum(["confirmed"]),
10941
+ contextKey: import_zod42.z.string()
10942
+ });
10943
+ var AutoFillResultSchema = import_zod42.z.object({
10944
+ matches: import_zod42.z.array(AutoFillMatchSchema)
10945
+ });
10946
+ var QuestionBatchResultSchema = import_zod42.z.object({
10947
+ batches: import_zod42.z.array(import_zod42.z.array(import_zod42.z.string()).describe("Array of field IDs in this batch"))
10948
+ });
10949
+ var LookupRequestSchema = import_zod42.z.object({
10950
+ type: import_zod42.z.string().describe("Type of lookup: 'records', 'website', 'policy'"),
10951
+ description: import_zod42.z.string(),
10952
+ url: import_zod42.z.string().optional(),
10953
+ targetFieldIds: import_zod42.z.array(import_zod42.z.string())
10954
+ });
10955
+ var ReplyIntentSchema = import_zod42.z.object({
10956
+ primaryIntent: import_zod42.z.enum(["answers_only", "question", "lookup_request", "mixed"]),
10957
+ hasAnswers: import_zod42.z.boolean(),
10958
+ questionText: import_zod42.z.string().optional(),
10959
+ questionFieldIds: import_zod42.z.array(import_zod42.z.string()).optional(),
10960
+ lookupRequests: import_zod42.z.array(LookupRequestSchema).optional()
10961
+ });
10962
+ var ParsedAnswerSchema = import_zod42.z.object({
10963
+ fieldId: import_zod42.z.string(),
10964
+ value: import_zod42.z.string(),
10965
+ explanation: import_zod42.z.string().optional()
10966
+ });
10967
+ var AnswerParsingResultSchema = import_zod42.z.object({
10968
+ answers: import_zod42.z.array(ParsedAnswerSchema),
10969
+ unanswered: import_zod42.z.array(import_zod42.z.string()).describe("Field IDs that were not answered")
10970
+ });
10971
+ var LookupFillSchema = import_zod42.z.object({
10972
+ fieldId: import_zod42.z.string(),
10973
+ value: import_zod42.z.string(),
10974
+ source: import_zod42.z.string().describe("Specific citable reference, e.g. 'GL Policy #POL-12345 (Hartford)'"),
10975
+ sourceSpanIds: import_zod42.z.array(import_zod42.z.string()).optional()
10976
+ });
10977
+ var LookupFillResultSchema = import_zod42.z.object({
10978
+ fills: import_zod42.z.array(LookupFillSchema),
10979
+ unfillable: import_zod42.z.array(import_zod42.z.string()),
10980
+ explanation: import_zod42.z.string().optional()
10981
+ });
10982
+ var FlatPdfPlacementSchema = import_zod42.z.object({
10983
+ fieldId: import_zod42.z.string(),
10984
+ page: import_zod42.z.number(),
10985
+ x: import_zod42.z.number().describe("Percentage from left edge (0-100)"),
10986
+ y: import_zod42.z.number().describe("Percentage from top edge (0-100)"),
10987
+ text: import_zod42.z.string(),
10988
+ fontSize: import_zod42.z.number().optional(),
10989
+ isCheckmark: import_zod42.z.boolean().optional()
10990
+ });
10991
+ var AcroFormMappingSchema = import_zod42.z.object({
10992
+ fieldId: import_zod42.z.string(),
10993
+ acroFormName: import_zod42.z.string(),
10994
+ value: import_zod42.z.string()
10995
+ });
10996
+ var QualityGateStatusSchema = import_zod42.z.enum(["passed", "warning", "failed"]);
10997
+ var QualitySeveritySchema = import_zod42.z.enum(["info", "warning", "blocking"]);
10998
+ var ApplicationQualityIssueSchema = import_zod42.z.object({
10999
+ code: import_zod42.z.string(),
9908
11000
  severity: QualitySeveritySchema,
9909
- message: import_zod41.z.string(),
9910
- fieldId: import_zod41.z.string().optional()
11001
+ message: import_zod42.z.string(),
11002
+ fieldId: import_zod42.z.string().optional()
9911
11003
  });
9912
- var ApplicationQualityRoundSchema = import_zod41.z.object({
9913
- round: import_zod41.z.number(),
9914
- kind: import_zod41.z.string(),
11004
+ var ApplicationQualityRoundSchema = import_zod42.z.object({
11005
+ round: import_zod42.z.number(),
11006
+ kind: import_zod42.z.string(),
9915
11007
  status: QualityGateStatusSchema,
9916
- summary: import_zod41.z.string().optional()
11008
+ summary: import_zod42.z.string().optional()
9917
11009
  });
9918
- var ApplicationQualityArtifactSchema = import_zod41.z.object({
9919
- kind: import_zod41.z.string(),
9920
- label: import_zod41.z.string().optional(),
9921
- itemCount: import_zod41.z.number().optional()
11010
+ var ApplicationQualityArtifactSchema = import_zod42.z.object({
11011
+ kind: import_zod42.z.string(),
11012
+ label: import_zod42.z.string().optional(),
11013
+ itemCount: import_zod42.z.number().optional()
9922
11014
  });
9923
- var ApplicationEmailReviewSchema = import_zod41.z.object({
9924
- issues: import_zod41.z.array(ApplicationQualityIssueSchema),
11015
+ var ApplicationEmailReviewSchema = import_zod42.z.object({
11016
+ issues: import_zod42.z.array(ApplicationQualityIssueSchema),
9925
11017
  qualityGateStatus: QualityGateStatusSchema
9926
11018
  });
9927
- var ApplicationQualityReportSchema = import_zod41.z.object({
9928
- issues: import_zod41.z.array(ApplicationQualityIssueSchema),
9929
- rounds: import_zod41.z.array(ApplicationQualityRoundSchema).optional(),
9930
- artifacts: import_zod41.z.array(ApplicationQualityArtifactSchema).optional(),
11019
+ var ApplicationQualityReportSchema = import_zod42.z.object({
11020
+ issues: import_zod42.z.array(ApplicationQualityIssueSchema),
11021
+ rounds: import_zod42.z.array(ApplicationQualityRoundSchema).optional(),
11022
+ artifacts: import_zod42.z.array(ApplicationQualityArtifactSchema).optional(),
9931
11023
  emailReview: ApplicationEmailReviewSchema.optional(),
9932
11024
  qualityGateStatus: QualityGateStatusSchema
9933
11025
  });
9934
- var ApplicationStateSchema = import_zod41.z.object({
9935
- id: import_zod41.z.string(),
9936
- pdfBase64: import_zod41.z.string().optional().describe("Original PDF, omitted after extraction"),
9937
- title: import_zod41.z.string().optional(),
9938
- applicationType: import_zod41.z.string().nullable().optional(),
9939
- fields: import_zod41.z.array(ApplicationFieldSchema),
9940
- batches: import_zod41.z.array(import_zod41.z.array(import_zod41.z.string())).optional(),
9941
- currentBatchIndex: import_zod41.z.number().default(0),
11026
+ var ApplicationStateSchema = import_zod42.z.object({
11027
+ id: import_zod42.z.string(),
11028
+ pdfBase64: import_zod42.z.string().optional().describe("Original PDF, omitted after extraction"),
11029
+ title: import_zod42.z.string().optional(),
11030
+ applicationType: import_zod42.z.string().nullable().optional(),
11031
+ fields: import_zod42.z.array(ApplicationFieldSchema),
11032
+ batches: import_zod42.z.array(import_zod42.z.array(import_zod42.z.string())).optional(),
11033
+ currentBatchIndex: import_zod42.z.number().default(0),
9942
11034
  qualityReport: ApplicationQualityReportSchema.optional(),
9943
- status: import_zod41.z.enum(["classifying", "extracting", "auto_filling", "batching", "collecting", "confirming", "mapping", "complete"]),
9944
- createdAt: import_zod41.z.number(),
9945
- updatedAt: import_zod41.z.number()
11035
+ status: import_zod42.z.enum(["classifying", "extracting", "auto_filling", "batching", "collecting", "confirming", "mapping", "complete"]),
11036
+ createdAt: import_zod42.z.number(),
11037
+ updatedAt: import_zod42.z.number()
9946
11038
  });
9947
11039
 
9948
11040
  // src/application/agents/classifier.ts
@@ -11297,104 +12389,106 @@ Respond with the final answer, deduplicated citations array, overall confidence
11297
12389
  }
11298
12390
 
11299
12391
  // src/schemas/query.ts
11300
- var import_zod42 = require("zod");
11301
- var QueryIntentSchema = import_zod42.z.enum([
12392
+ var import_zod43 = require("zod");
12393
+ var QueryIntentSchema = import_zod43.z.enum([
11302
12394
  "policy_question",
11303
12395
  "coverage_comparison",
11304
12396
  "document_search",
11305
12397
  "claims_inquiry",
11306
12398
  "general_knowledge"
11307
12399
  ]);
11308
- var QueryAttachmentKindSchema = import_zod42.z.enum(["image", "pdf", "text"]);
11309
- var QueryAttachmentSchema = import_zod42.z.object({
11310
- id: import_zod42.z.string().optional().describe("Optional stable attachment ID from the caller"),
12400
+ var QueryAttachmentKindSchema = import_zod43.z.enum(["image", "pdf", "text"]);
12401
+ var QueryAttachmentSchema = import_zod43.z.object({
12402
+ id: import_zod43.z.string().optional().describe("Optional stable attachment ID from the caller"),
11311
12403
  kind: QueryAttachmentKindSchema,
11312
- name: import_zod42.z.string().optional().describe("Original filename or user-facing label"),
11313
- mimeType: import_zod42.z.string().optional().describe("MIME type such as image/jpeg or application/pdf"),
11314
- base64: import_zod42.z.string().optional().describe("Base64-encoded file content for image/pdf attachments"),
11315
- text: import_zod42.z.string().optional().describe("Plain-text attachment content when available"),
11316
- description: import_zod42.z.string().optional().describe("Caller-provided description of the attachment")
12404
+ name: import_zod43.z.string().optional().describe("Original filename or user-facing label"),
12405
+ mimeType: import_zod43.z.string().optional().describe("MIME type such as image/jpeg or application/pdf"),
12406
+ base64: import_zod43.z.string().optional().describe("Base64-encoded file content for image/pdf attachments"),
12407
+ text: import_zod43.z.string().optional().describe("Plain-text attachment content when available"),
12408
+ description: import_zod43.z.string().optional().describe("Caller-provided description of the attachment")
11317
12409
  });
11318
- var QueryRetrievalModeSchema = import_zod42.z.enum([
12410
+ var QueryRetrievalModeSchema = import_zod43.z.enum([
11319
12411
  "graph_only",
11320
12412
  "source_rag",
11321
12413
  "long_context",
11322
12414
  "hybrid"
11323
12415
  ]);
11324
- var SubQuestionSchema = import_zod42.z.object({
11325
- question: import_zod42.z.string().describe("Atomic sub-question to retrieve and answer independently"),
12416
+ var SubQuestionSchema = import_zod43.z.object({
12417
+ question: import_zod43.z.string().describe("Atomic sub-question to retrieve and answer independently"),
11326
12418
  intent: QueryIntentSchema,
11327
- chunkTypes: import_zod42.z.array(import_zod42.z.string()).optional().describe("Chunk types to filter retrieval (e.g. coverage, endorsement, declaration)"),
11328
- documentFilters: import_zod42.z.object({
11329
- type: import_zod42.z.enum(["policy", "quote"]).optional(),
11330
- carrier: import_zod42.z.string().optional(),
11331
- insuredName: import_zod42.z.string().optional(),
11332
- policyNumber: import_zod42.z.string().optional(),
11333
- quoteNumber: import_zod42.z.string().optional(),
11334
- policyTypes: import_zod42.z.array(PolicyTypeSchema).optional().describe("Filter by policy type (e.g. homeowners_ho3, renters_ho4, pet) to avoid mixing up similar policies")
12419
+ chunkTypes: import_zod43.z.array(import_zod43.z.string()).optional().describe("Chunk types to filter retrieval (e.g. coverage, endorsement, declaration)"),
12420
+ documentFilters: import_zod43.z.object({
12421
+ type: import_zod43.z.enum(["policy", "quote"]).optional(),
12422
+ carrier: import_zod43.z.string().optional(),
12423
+ insuredName: import_zod43.z.string().optional(),
12424
+ policyNumber: import_zod43.z.string().optional(),
12425
+ quoteNumber: import_zod43.z.string().optional(),
12426
+ policyTypes: import_zod43.z.array(PolicyTypeSchema).optional().describe("Filter by policy type (e.g. homeowners_ho3, renters_ho4, pet) to avoid mixing up similar policies")
11335
12427
  }).optional().describe("Structured filters to narrow document lookup")
11336
12428
  });
11337
- var QueryClassifyResultSchema = import_zod42.z.object({
12429
+ var QueryClassifyResultSchema = import_zod43.z.object({
11338
12430
  intent: QueryIntentSchema,
11339
- subQuestions: import_zod42.z.array(SubQuestionSchema).min(1).describe("Decomposed atomic sub-questions"),
11340
- requiresDocumentLookup: import_zod42.z.boolean().describe("Whether structured document lookup is needed"),
11341
- requiresChunkSearch: import_zod42.z.boolean().describe("Whether semantic chunk search is needed"),
11342
- requiresConversationHistory: import_zod42.z.boolean().describe("Whether conversation history is relevant"),
12431
+ subQuestions: import_zod43.z.array(SubQuestionSchema).min(1).describe("Decomposed atomic sub-questions"),
12432
+ requiresDocumentLookup: import_zod43.z.boolean().describe("Whether structured document lookup is needed"),
12433
+ requiresChunkSearch: import_zod43.z.boolean().describe("Whether semantic chunk search is needed"),
12434
+ requiresConversationHistory: import_zod43.z.boolean().describe("Whether conversation history is relevant"),
11343
12435
  retrievalMode: QueryRetrievalModeSchema.optional().describe("Preferred retrieval strategy for the query when source-span retrieval is available")
11344
12436
  });
11345
- var EvidenceItemSchema = import_zod42.z.object({
11346
- source: import_zod42.z.enum(["chunk", "document", "conversation", "attachment", "source_span"]),
11347
- chunkId: import_zod42.z.string().optional(),
11348
- sourceSpanId: import_zod42.z.string().optional(),
11349
- documentId: import_zod42.z.string().optional(),
11350
- turnId: import_zod42.z.string().optional(),
11351
- attachmentId: import_zod42.z.string().optional(),
11352
- text: import_zod42.z.string().describe("Text excerpt from the source"),
11353
- relevance: import_zod42.z.number().min(0).max(1),
12437
+ var EvidenceItemSchema = import_zod43.z.object({
12438
+ source: import_zod43.z.enum(["chunk", "document", "conversation", "attachment", "source_span", "source_node"]),
12439
+ chunkId: import_zod43.z.string().optional(),
12440
+ sourceNodeId: import_zod43.z.string().optional(),
12441
+ sourceSpanId: import_zod43.z.string().optional(),
12442
+ documentId: import_zod43.z.string().optional(),
12443
+ turnId: import_zod43.z.string().optional(),
12444
+ attachmentId: import_zod43.z.string().optional(),
12445
+ text: import_zod43.z.string().describe("Text excerpt from the source"),
12446
+ relevance: import_zod43.z.number().min(0).max(1),
11354
12447
  retrievalMode: QueryRetrievalModeSchema.optional(),
11355
12448
  sourceLocation: SourceSpanLocationSchema.optional(),
11356
- metadata: import_zod42.z.array(import_zod42.z.object({ key: import_zod42.z.string(), value: import_zod42.z.string() })).optional()
11357
- });
11358
- var AttachmentInterpretationSchema = import_zod42.z.object({
11359
- summary: import_zod42.z.string().describe("Concise summary of what the attachment shows or contains"),
11360
- extractedFacts: import_zod42.z.array(import_zod42.z.string()).describe("Specific observable or document facts grounded in the attachment"),
11361
- recommendedFocus: import_zod42.z.array(import_zod42.z.string()).describe("Important details to incorporate when answering follow-up questions"),
11362
- confidence: import_zod42.z.number().min(0).max(1)
11363
- });
11364
- var RetrievalResultSchema = import_zod42.z.object({
11365
- subQuestion: import_zod42.z.string(),
11366
- evidence: import_zod42.z.array(EvidenceItemSchema)
11367
- });
11368
- var CitationSchema = import_zod42.z.object({
11369
- index: import_zod42.z.number().describe("Citation number [1], [2], etc."),
11370
- chunkId: import_zod42.z.string().optional().describe("Source chunk ID, e.g. doc-123:coverage:2"),
11371
- sourceSpanId: import_zod42.z.string().optional().describe("Precise source span ID when available"),
11372
- documentId: import_zod42.z.string(),
11373
- documentType: import_zod42.z.enum(["policy", "quote"]).optional(),
11374
- field: import_zod42.z.string().optional().describe("Specific field path, e.g. coverages[0].deductible"),
11375
- quote: import_zod42.z.string().describe("Exact text from source that supports the claim"),
11376
- relevance: import_zod42.z.number().min(0).max(1),
12449
+ metadata: import_zod43.z.array(import_zod43.z.object({ key: import_zod43.z.string(), value: import_zod43.z.string() })).optional()
12450
+ });
12451
+ var AttachmentInterpretationSchema = import_zod43.z.object({
12452
+ summary: import_zod43.z.string().describe("Concise summary of what the attachment shows or contains"),
12453
+ extractedFacts: import_zod43.z.array(import_zod43.z.string()).describe("Specific observable or document facts grounded in the attachment"),
12454
+ recommendedFocus: import_zod43.z.array(import_zod43.z.string()).describe("Important details to incorporate when answering follow-up questions"),
12455
+ confidence: import_zod43.z.number().min(0).max(1)
12456
+ });
12457
+ var RetrievalResultSchema = import_zod43.z.object({
12458
+ subQuestion: import_zod43.z.string(),
12459
+ evidence: import_zod43.z.array(EvidenceItemSchema)
12460
+ });
12461
+ var CitationSchema = import_zod43.z.object({
12462
+ index: import_zod43.z.number().describe("Citation number [1], [2], etc."),
12463
+ chunkId: import_zod43.z.string().optional().describe("Source chunk ID, e.g. doc-123:coverage:2"),
12464
+ sourceNodeId: import_zod43.z.string().optional().describe("Source tree node ID when available"),
12465
+ sourceSpanId: import_zod43.z.string().optional().describe("Precise source span ID when available"),
12466
+ documentId: import_zod43.z.string(),
12467
+ documentType: import_zod43.z.enum(["policy", "quote"]).optional(),
12468
+ field: import_zod43.z.string().optional().describe("Specific field path, e.g. coverages[0].deductible"),
12469
+ quote: import_zod43.z.string().describe("Exact text from source that supports the claim"),
12470
+ relevance: import_zod43.z.number().min(0).max(1),
11377
12471
  retrievalMode: QueryRetrievalModeSchema.optional(),
11378
12472
  sourceLocation: SourceSpanLocationSchema.optional()
11379
12473
  });
11380
- var SubAnswerSchema = import_zod42.z.object({
11381
- subQuestion: import_zod42.z.string(),
11382
- answer: import_zod42.z.string(),
11383
- citations: import_zod42.z.array(CitationSchema),
11384
- confidence: import_zod42.z.number().min(0).max(1),
11385
- needsMoreContext: import_zod42.z.boolean().describe("True if evidence was insufficient to answer fully")
11386
- });
11387
- var VerifyResultSchema = import_zod42.z.object({
11388
- approved: import_zod42.z.boolean().describe("Whether all sub-answers are adequately grounded"),
11389
- issues: import_zod42.z.array(import_zod42.z.string()).describe("Specific grounding or consistency issues found"),
11390
- retrySubQuestions: import_zod42.z.array(import_zod42.z.string()).optional().describe("Sub-questions that need additional retrieval or re-reasoning")
11391
- });
11392
- var QueryResultSchema = import_zod42.z.object({
11393
- answer: import_zod42.z.string(),
11394
- citations: import_zod42.z.array(CitationSchema),
12474
+ var SubAnswerSchema = import_zod43.z.object({
12475
+ subQuestion: import_zod43.z.string(),
12476
+ answer: import_zod43.z.string(),
12477
+ citations: import_zod43.z.array(CitationSchema),
12478
+ confidence: import_zod43.z.number().min(0).max(1),
12479
+ needsMoreContext: import_zod43.z.boolean().describe("True if evidence was insufficient to answer fully")
12480
+ });
12481
+ var VerifyResultSchema = import_zod43.z.object({
12482
+ approved: import_zod43.z.boolean().describe("Whether all sub-answers are adequately grounded"),
12483
+ issues: import_zod43.z.array(import_zod43.z.string()).describe("Specific grounding or consistency issues found"),
12484
+ retrySubQuestions: import_zod43.z.array(import_zod43.z.string()).optional().describe("Sub-questions that need additional retrieval or re-reasoning")
12485
+ });
12486
+ var QueryResultSchema = import_zod43.z.object({
12487
+ answer: import_zod43.z.string(),
12488
+ citations: import_zod43.z.array(CitationSchema),
11395
12489
  intent: QueryIntentSchema,
11396
- confidence: import_zod42.z.number().min(0).max(1),
11397
- followUp: import_zod42.z.string().optional().describe("Suggested follow-up question if applicable")
12490
+ confidence: import_zod43.z.number().min(0).max(1),
12491
+ followUp: import_zod43.z.string().optional().describe("Suggested follow-up question if applicable")
11398
12492
  });
11399
12493
 
11400
12494
  // src/query/retriever.ts
@@ -11409,6 +12503,35 @@ async function retrieve(subQuestion, conversationId, config) {
11409
12503
  tasks.push(
11410
12504
  (async () => {
11411
12505
  try {
12506
+ const nodeResults = await sourceRetriever?.searchSourceNodes?.({
12507
+ question: subQuestion.question,
12508
+ limit: retrievalLimit,
12509
+ mode: retrievalMode
12510
+ }) ?? [];
12511
+ for (const result of nodeResults) {
12512
+ const hierarchyText = result.hierarchy.map((node) => `${node.path} ${node.title}: ${node.textExcerpt ?? node.description}`).join("\n");
12513
+ const spanText = result.spans.map((span) => `[source-span:${span.id}${span.pageStart ? ` p.${span.pageStart}` : ""}]
12514
+ ${span.text}`).join("\n\n");
12515
+ evidence.push({
12516
+ source: "source_node",
12517
+ sourceNodeId: result.node.id,
12518
+ sourceSpanId: result.spans[0]?.id,
12519
+ documentId: result.node.documentId,
12520
+ text: [hierarchyText, spanText].filter(Boolean).join("\n\n"),
12521
+ relevance: result.relevance,
12522
+ retrievalMode,
12523
+ sourceLocation: result.spans[0]?.location ?? (result.node.pageStart ? { page: result.node.pageStart } : void 0),
12524
+ metadata: [
12525
+ { key: "kind", value: result.node.kind },
12526
+ { key: "path", value: result.node.path },
12527
+ { key: "title", value: result.node.title },
12528
+ ...result.node.metadata ? recordToKVArray(Object.fromEntries(
12529
+ Object.entries(result.node.metadata).filter(([, value]) => typeof value === "string").map(([key, value]) => [key, value])
12530
+ )) : []
12531
+ ]
12532
+ });
12533
+ }
12534
+ if (nodeResults.length > 0) return;
11412
12535
  const sourceResults = await sourceRetriever?.searchSourceSpans({
11413
12536
  question: subQuestion.question,
11414
12537
  limit: retrievalLimit,
@@ -11428,7 +12551,7 @@ async function retrieve(subQuestion, conversationId, config) {
11428
12551
  });
11429
12552
  }
11430
12553
  } catch (e) {
11431
- await log?.(`Source span search failed for "${subQuestion.question}": ${e}`);
12554
+ await log?.(`Source tree search failed for "${subQuestion.question}": ${e}`);
11432
12555
  }
11433
12556
  })()
11434
12557
  );
@@ -11630,7 +12753,7 @@ Answer the sub-question based on the evidence above. For every factual claim, in
11630
12753
  async function reason(subQuestion, intent, evidence, config) {
11631
12754
  const { generateObject, providerOptions } = config;
11632
12755
  const evidenceText = evidence.map((e, i) => {
11633
- const sourceLabel = e.source === "source_span" ? `[source-span:${e.sourceSpanId}]` : e.source === "chunk" ? `[chunk:${e.chunkId}]` : e.source === "document" ? `[doc:${e.documentId}]` : e.source === "attachment" ? `[attachment:${e.attachmentId}]` : `[turn:${e.turnId}]`;
12756
+ const sourceLabel = e.source === "source_node" ? `[source-node:${e.sourceNodeId} source-span:${e.sourceSpanId ?? "none"}]` : e.source === "source_span" ? `[source-span:${e.sourceSpanId}]` : e.source === "chunk" ? `[chunk:${e.chunkId}]` : e.source === "document" ? `[doc:${e.documentId}]` : e.source === "attachment" ? `[attachment:${e.attachmentId}]` : `[turn:${e.turnId}]`;
11634
12757
  return `Evidence ${i + 1} ${sourceLabel} (relevance: ${e.relevance.toFixed(2)}):
11635
12758
  ${e.text}`;
11636
12759
  }).join("\n\n");
@@ -12432,7 +13555,7 @@ ${sa.answer}`).join("\n\n"),
12432
13555
  }
12433
13556
 
12434
13557
  // src/pce/index.ts
12435
- var import_zod43 = require("zod");
13558
+ var import_zod44 = require("zod");
12436
13559
 
12437
13560
  // src/prompts/pce/index.ts
12438
13561
  function buildPceNormalizePrompt(input) {
@@ -12466,11 +13589,11 @@ ${input.openQuestions.map((question) => `- ${question.id}${question.fieldPath ?
12466
13589
  }
12467
13590
 
12468
13591
  // src/pce/index.ts
12469
- var ReplyAnswersSchema = import_zod43.z.object({
12470
- answers: import_zod43.z.array(import_zod43.z.object({
12471
- questionId: import_zod43.z.string().optional(),
12472
- fieldPath: import_zod43.z.string().optional(),
12473
- answer: import_zod43.z.string()
13592
+ var ReplyAnswersSchema = import_zod44.z.object({
13593
+ answers: import_zod44.z.array(import_zod44.z.object({
13594
+ questionId: import_zod44.z.string().optional(),
13595
+ fieldPath: import_zod44.z.string().optional(),
13596
+ answer: import_zod44.z.string()
12474
13597
  }))
12475
13598
  });
12476
13599
  function createPceAgent(config = {}) {
@@ -13301,6 +14424,8 @@ var AGENT_TOOLS = [
13301
14424
  DocumentMetadataSchema,
13302
14425
  DocumentNodeSchema,
13303
14426
  DocumentPageMapEntrySchema,
14427
+ DocumentSourceNodeKindSchema,
14428
+ DocumentSourceNodeSchema,
13304
14429
  DocumentTableOfContentsEntrySchema,
13305
14430
  DocumentTypeSchema,
13306
14431
  DriverRecordSchema,
@@ -13356,6 +14481,9 @@ var AGENT_TOOLS = [
13356
14481
  MemorySourceStore,
13357
14482
  MissingInfoQuestionSchema,
13358
14483
  NamedInsuredSchema,
14484
+ OperationalCoverageLineSchema,
14485
+ OperationalEndorsementSupportSchema,
14486
+ OperationalPartySchema,
13359
14487
  PERSONAL_AUTO_USAGES,
13360
14488
  PET_SPECIES,
13361
14489
  PLATFORM_CONFIGS,
@@ -13385,6 +14513,7 @@ var AGENT_TOOLS = [
13385
14513
  PolicyChangeStatusSchema,
13386
14514
  PolicyConditionSchema,
13387
14515
  PolicyDocumentSchema,
14516
+ PolicyOperationalProfileSchema,
13388
14517
  PolicySectionTypeSchema,
13389
14518
  PolicyTermTypeSchema,
13390
14519
  PolicyTypeSchema,
@@ -13416,6 +14545,7 @@ var AGENT_TOOLS = [
13416
14545
  ScheduledItemCategorySchema,
13417
14546
  SectionSchema,
13418
14547
  SharedLimitSchema,
14548
+ SourceBackedValueSchema,
13419
14549
  SourceChunkSchema,
13420
14550
  SourceKindSchema,
13421
14551
  SourceSpanBBoxSchema,
@@ -13457,7 +14587,9 @@ var AGENT_TOOLS = [
13457
14587
  buildConfirmationSummaryPrompt,
13458
14588
  buildConversationMemoryGuidance,
13459
14589
  buildCoverageGapPrompt,
14590
+ buildDeterministicOperationalProfile,
13460
14591
  buildDoclingProviderOptions,
14592
+ buildDocumentSourceTree,
13461
14593
  buildFieldExplanationPrompt,
13462
14594
  buildFieldExtractionPrompt,
13463
14595
  buildFlatPdfMappingPrompt,
@@ -13506,9 +14638,11 @@ var AGENT_TOOLS = [
13506
14638
  getTemplate,
13507
14639
  isDoclingExtractionInput,
13508
14640
  isFileReference,
14641
+ mergeOperationalProfile,
13509
14642
  mergeQuestionAnswers,
13510
14643
  mergeSourceSpans,
13511
14644
  normalizeDoclingDocument,
14645
+ normalizeDocumentSourceTreePaths,
13512
14646
  normalizeForMatch,
13513
14647
  orderSourceEvidence,
13514
14648
  overlayTextOnPdf,