@claritylabs/cl-sdk 2.0.1 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1407,6 +1407,8 @@ var DocumentAgentGuidanceSchema = z16.object({
1407
1407
  sourceSpanIds: z16.array(z16.string()).optional()
1408
1408
  });
1409
1409
  var DocumentMetadataSchema = z16.object({
1410
+ sourceTreeVersion: z16.string().optional(),
1411
+ sourceTreeCanonical: z16.boolean().optional(),
1410
1412
  formInventory: z16.array(FormReferenceSchema).optional(),
1411
1413
  tableOfContents: z16.array(DocumentTableOfContentsEntrySchema).optional(),
1412
1414
  pageMap: z16.array(DocumentPageMapEntrySchema).optional(),
@@ -2006,6 +2008,86 @@ var SourceChunkSchema = z20.object({
2006
2008
  pageEnd: z20.number().int().positive().optional(),
2007
2009
  metadata: z20.record(z20.string(), z20.string()).default({})
2008
2010
  });
2011
+ var DocumentSourceNodeKindSchema = z20.enum([
2012
+ "document",
2013
+ "page_group",
2014
+ "page",
2015
+ "form",
2016
+ "endorsement",
2017
+ "section",
2018
+ "schedule",
2019
+ "clause",
2020
+ "table",
2021
+ "table_row",
2022
+ "table_cell",
2023
+ "text"
2024
+ ]);
2025
+ var DocumentSourceNodeSchema = z20.object({
2026
+ id: z20.string().min(1),
2027
+ documentId: z20.string().min(1),
2028
+ parentId: z20.string().optional(),
2029
+ kind: DocumentSourceNodeKindSchema,
2030
+ title: z20.string(),
2031
+ description: z20.string(),
2032
+ textExcerpt: z20.string().optional(),
2033
+ sourceSpanIds: z20.array(z20.string().min(1)),
2034
+ pageStart: z20.number().int().positive().optional(),
2035
+ pageEnd: z20.number().int().positive().optional(),
2036
+ bbox: z20.array(SourceSpanBBoxSchema).optional(),
2037
+ order: z20.number().int().nonnegative(),
2038
+ path: z20.string(),
2039
+ metadata: z20.record(z20.string(), z20.unknown()).optional()
2040
+ });
2041
+ var SourceBackedValueSchema = z20.object({
2042
+ value: z20.string(),
2043
+ normalizedValue: z20.string().optional(),
2044
+ confidence: z20.enum(["low", "medium", "high"]).default("medium"),
2045
+ sourceNodeIds: z20.array(z20.string().min(1)).default([]),
2046
+ sourceSpanIds: z20.array(z20.string().min(1)).default([])
2047
+ });
2048
+ var OperationalCoverageLineSchema = z20.object({
2049
+ name: z20.string(),
2050
+ coverageCode: z20.string().optional(),
2051
+ limit: z20.string().optional(),
2052
+ deductible: z20.string().optional(),
2053
+ premium: z20.string().optional(),
2054
+ formNumber: z20.string().optional(),
2055
+ sectionRef: z20.string().optional(),
2056
+ sourceNodeIds: z20.array(z20.string().min(1)).default([]),
2057
+ sourceSpanIds: z20.array(z20.string().min(1)).default([])
2058
+ });
2059
+ var OperationalPartySchema = z20.object({
2060
+ role: z20.string(),
2061
+ name: z20.string(),
2062
+ sourceNodeIds: z20.array(z20.string().min(1)).default([]),
2063
+ sourceSpanIds: z20.array(z20.string().min(1)).default([])
2064
+ });
2065
+ var OperationalEndorsementSupportSchema = z20.object({
2066
+ kind: z20.string(),
2067
+ status: z20.enum(["supported", "excluded", "requires_review"]),
2068
+ summary: z20.string(),
2069
+ sourceNodeIds: z20.array(z20.string().min(1)).default([]),
2070
+ sourceSpanIds: z20.array(z20.string().min(1)).default([])
2071
+ });
2072
+ var PolicyOperationalProfileSchema = z20.object({
2073
+ documentType: z20.enum(["policy", "quote"]).default("policy"),
2074
+ policyTypes: z20.array(z20.string()).default(["other"]),
2075
+ policyNumber: SourceBackedValueSchema.optional(),
2076
+ namedInsured: SourceBackedValueSchema.optional(),
2077
+ insurer: SourceBackedValueSchema.optional(),
2078
+ broker: SourceBackedValueSchema.optional(),
2079
+ effectiveDate: SourceBackedValueSchema.optional(),
2080
+ expirationDate: SourceBackedValueSchema.optional(),
2081
+ retroactiveDate: SourceBackedValueSchema.optional(),
2082
+ premium: SourceBackedValueSchema.optional(),
2083
+ coverageTypes: z20.array(z20.string()).default([]),
2084
+ coverages: z20.array(OperationalCoverageLineSchema).default([]),
2085
+ parties: z20.array(OperationalPartySchema).default([]),
2086
+ endorsementSupport: z20.array(OperationalEndorsementSupportSchema).default([]),
2087
+ sourceNodeIds: z20.array(z20.string().min(1)).default([]),
2088
+ sourceSpanIds: z20.array(z20.string().min(1)).default([]),
2089
+ warnings: z20.array(z20.string()).default([])
2090
+ });
2009
2091
 
2010
2092
  // src/source/ids.ts
2011
2093
  function normalizeText(text) {
@@ -2189,8 +2271,8 @@ function chunkSourceSpans(spans, options = {}) {
2189
2271
  if (current.length === 0) return;
2190
2272
  const text = current.map((span) => span.text).join("\n\n");
2191
2273
  const textHash = sourceSpanTextHash(text);
2192
- const pageStart = firstNumber(current.map((span) => span.pageStart));
2193
- const pageEnd = lastNumber(current.map((span) => span.pageEnd ?? span.pageStart));
2274
+ const pageStart2 = firstNumber(current.map((span) => span.pageStart));
2275
+ const pageEnd2 = lastNumber(current.map((span) => span.pageEnd ?? span.pageStart));
2194
2276
  const chunk = {
2195
2277
  id: `${sanitizeIdPart(current[0].documentId)}:source_chunk:${chunks.length}:${stableHash2({
2196
2278
  sourceSpanIds: current.map((span) => span.id),
@@ -2200,8 +2282,8 @@ function chunkSourceSpans(spans, options = {}) {
2200
2282
  sourceSpanIds: current.map((span) => span.id),
2201
2283
  text,
2202
2284
  textHash,
2203
- pageStart,
2204
- pageEnd,
2285
+ pageStart: pageStart2,
2286
+ pageEnd: pageEnd2,
2205
2287
  metadata: mergeMetadata(current)
2206
2288
  };
2207
2289
  chunks.push(SourceChunkSchema.parse(chunk));
@@ -2354,6 +2436,542 @@ function matchesFilters(span, filters) {
2354
2436
  return true;
2355
2437
  }
2356
2438
 
2439
+ // src/source/tree.ts
2440
+ function normalizeWhitespace2(value) {
2441
+ return value.replace(/\s+/g, " ").trim();
2442
+ }
2443
+ function sanitizeIdPart2(value) {
2444
+ return value.replace(/[^a-zA-Z0-9_.:-]/g, "_");
2445
+ }
2446
+ function truncate(value, maxChars) {
2447
+ const text = normalizeWhitespace2(value);
2448
+ return text.length > maxChars ? `${text.slice(0, maxChars).trimEnd()}...` : text;
2449
+ }
2450
+ function pageStart(span) {
2451
+ return span.pageStart ?? span.location?.page ?? span.location?.startPage;
2452
+ }
2453
+ function pageEnd(span) {
2454
+ return span.pageEnd ?? span.location?.endPage ?? pageStart(span);
2455
+ }
2456
+ function sourceUnit2(span) {
2457
+ return span.sourceUnit ?? span.metadata?.sourceUnit ?? span.metadata?.elementType;
2458
+ }
2459
+ function elementType(span) {
2460
+ return span.metadata?.elementType ?? span.metadata?.sourceUnit ?? span.sourceUnit;
2461
+ }
2462
+ function tableId(span) {
2463
+ return span.table?.tableId ?? span.metadata?.tableId;
2464
+ }
2465
+ function rowSpanId(span) {
2466
+ return span.parentSpanId ?? span.table?.rowSpanId ?? span.metadata?.rowSpanId;
2467
+ }
2468
+ function nodeId(documentId, kind, parts) {
2469
+ return [
2470
+ sanitizeIdPart2(documentId),
2471
+ "source_node",
2472
+ kind,
2473
+ stableHash2(parts.filter((part) => part !== void 0).join("|")).slice(0, 12)
2474
+ ].join(":");
2475
+ }
2476
+ function titleCase(value) {
2477
+ return value.replace(/[_-]+/g, " ").replace(/\b\w/g, (char) => char.toUpperCase()).trim();
2478
+ }
2479
+ function nodeTextDescription(params) {
2480
+ return [
2481
+ params.title,
2482
+ params.kind.replace(/_/g, " "),
2483
+ params.page ? `page ${params.page}` : void 0,
2484
+ params.formNumber ? `form ${params.formNumber}` : void 0,
2485
+ params.text ? truncate(params.text, 1200) : void 0
2486
+ ].filter(Boolean).join(" | ");
2487
+ }
2488
+ function normalizeNodeKind(span) {
2489
+ const unit = sourceUnit2(span);
2490
+ const element = elementType(span);
2491
+ if (unit === "page") return "page";
2492
+ if (unit === "table") return "table";
2493
+ if (unit === "table_row") return "table_row";
2494
+ if (unit === "table_cell") return "table_cell";
2495
+ if (unit === "key_value") return "schedule";
2496
+ if (unit === "section") return "section";
2497
+ if (element === "title" || element === "section_candidate") {
2498
+ const text = span.text.toLowerCase();
2499
+ if (/endorsement/.test(text)) return "endorsement";
2500
+ if (/schedule|declarations?/.test(text)) return "schedule";
2501
+ if (/clause|condition|exclusion|definition/.test(text)) return "clause";
2502
+ return "section";
2503
+ }
2504
+ return "text";
2505
+ }
2506
+ function pageNodeTitle(page) {
2507
+ return `Page ${page}`;
2508
+ }
2509
+ function makeNode(params) {
2510
+ return {
2511
+ id: params.id,
2512
+ documentId: params.documentId,
2513
+ parentId: params.parentId,
2514
+ kind: params.kind,
2515
+ title: params.title,
2516
+ description: params.description ?? nodeTextDescription({
2517
+ kind: params.kind,
2518
+ title: params.title,
2519
+ text: params.textExcerpt,
2520
+ page: params.pageStart,
2521
+ formNumber: typeof params.metadata?.formNumber === "string" ? params.metadata.formNumber : void 0
2522
+ }),
2523
+ textExcerpt: params.textExcerpt,
2524
+ sourceSpanIds: params.sourceSpanIds ?? [],
2525
+ pageStart: params.pageStart,
2526
+ pageEnd: params.pageEnd,
2527
+ bbox: params.bbox,
2528
+ order: params.order,
2529
+ path: "",
2530
+ metadata: params.metadata
2531
+ };
2532
+ }
2533
+ function sortSpans(left, right) {
2534
+ const leftPage = pageStart(left) ?? 0;
2535
+ const rightPage = pageStart(right) ?? 0;
2536
+ if (leftPage !== rightPage) return leftPage - rightPage;
2537
+ const leftRow = left.table?.rowIndex ?? Number(left.metadata?.rowIndex ?? 0);
2538
+ const rightRow = right.table?.rowIndex ?? Number(right.metadata?.rowIndex ?? 0);
2539
+ if (leftRow !== rightRow) return leftRow - rightRow;
2540
+ const leftCol = left.table?.columnIndex ?? Number(left.metadata?.columnIndex ?? 0);
2541
+ const rightCol = right.table?.columnIndex ?? Number(right.metadata?.columnIndex ?? 0);
2542
+ if (leftCol !== rightCol) return leftCol - rightCol;
2543
+ return left.id.localeCompare(right.id);
2544
+ }
2545
+ function normalizeDocumentSourceTreePaths(nodes) {
2546
+ const byParent = /* @__PURE__ */ new Map();
2547
+ for (const node of nodes) {
2548
+ const key = node.parentId;
2549
+ const group = byParent.get(key) ?? [];
2550
+ group.push(node);
2551
+ byParent.set(key, group);
2552
+ }
2553
+ for (const group of byParent.values()) {
2554
+ group.sort((left, right) => left.order - right.order || left.id.localeCompare(right.id));
2555
+ }
2556
+ const result = [];
2557
+ const visit = (node, path) => {
2558
+ const next = { ...node, path };
2559
+ result.push(next);
2560
+ const children = byParent.get(node.id) ?? [];
2561
+ children.forEach((child, index) => visit(child, `${path}.${index + 1}`));
2562
+ };
2563
+ const roots = byParent.get(void 0) ?? [];
2564
+ roots.forEach((root, index) => visit(root, String(index + 1)));
2565
+ return result;
2566
+ }
2567
+ function buildDocumentSourceTree(sourceSpans, documentId) {
2568
+ const orderedSpans = [...sourceSpans].sort(sortSpans);
2569
+ const resolvedDocumentId = documentId ?? orderedSpans[0]?.documentId ?? "document";
2570
+ const nodes = /* @__PURE__ */ new Map();
2571
+ let order = 0;
2572
+ const rootId = nodeId(resolvedDocumentId, "document", [resolvedDocumentId]);
2573
+ nodes.set(rootId, makeNode({
2574
+ id: rootId,
2575
+ documentId: resolvedDocumentId,
2576
+ kind: "document",
2577
+ title: "Document",
2578
+ description: "Document root for source-native policy hierarchy",
2579
+ sourceSpanIds: orderedSpans.map((span) => span.id).slice(0, 200),
2580
+ pageStart: orderedSpans.map(pageStart).find((value) => typeof value === "number"),
2581
+ pageEnd: [...orderedSpans].reverse().map(pageEnd).find((value) => typeof value === "number"),
2582
+ order: order++,
2583
+ metadata: { sourceTreeVersion: "v3" }
2584
+ }));
2585
+ const pageNodeIds = /* @__PURE__ */ new Map();
2586
+ const tableNodeIds = /* @__PURE__ */ new Map();
2587
+ const rowNodeIds = /* @__PURE__ */ new Map();
2588
+ const ensurePage = (page) => {
2589
+ const existing = pageNodeIds.get(page);
2590
+ if (existing) return existing;
2591
+ const id = nodeId(resolvedDocumentId, "page", [page]);
2592
+ const pageSpan = orderedSpans.find((span) => pageStart(span) === page && sourceUnit2(span) === "page");
2593
+ nodes.set(id, makeNode({
2594
+ id,
2595
+ documentId: resolvedDocumentId,
2596
+ parentId: rootId,
2597
+ kind: "page",
2598
+ title: pageNodeTitle(page),
2599
+ description: pageSpan ? nodeTextDescription({ kind: "page", title: pageNodeTitle(page), text: pageSpan.text, page }) : pageNodeTitle(page),
2600
+ textExcerpt: pageSpan ? truncate(pageSpan.text, 1600) : void 0,
2601
+ sourceSpanIds: pageSpan ? [pageSpan.id] : [],
2602
+ pageStart: page,
2603
+ pageEnd: page,
2604
+ bbox: pageSpan?.bbox,
2605
+ order: order++,
2606
+ metadata: { sourceUnit: "page" }
2607
+ }));
2608
+ pageNodeIds.set(page, id);
2609
+ return id;
2610
+ };
2611
+ const ensureTable = (span, pageParentId) => {
2612
+ const idSource = tableId(span) ?? `${span.documentId}:p${pageStart(span) ?? "na"}:table:${nodes.size}`;
2613
+ const existing = tableNodeIds.get(idSource);
2614
+ if (existing) return existing;
2615
+ const id = nodeId(resolvedDocumentId, "table", [idSource]);
2616
+ nodes.set(id, makeNode({
2617
+ id,
2618
+ documentId: resolvedDocumentId,
2619
+ parentId: pageParentId,
2620
+ kind: "table",
2621
+ title: `Table ${tableNodeIds.size + 1}`,
2622
+ description: `Table on page ${pageStart(span) ?? "unknown"} for source rows and cells`,
2623
+ sourceSpanIds: [],
2624
+ pageStart: pageStart(span),
2625
+ pageEnd: pageEnd(span),
2626
+ order: order++,
2627
+ metadata: { tableId: idSource, sourceUnit: "table" }
2628
+ }));
2629
+ tableNodeIds.set(idSource, id);
2630
+ return id;
2631
+ };
2632
+ const addStandaloneSpanNode = (span, parentId) => {
2633
+ const kind = normalizeNodeKind(span);
2634
+ if (kind === "page") return;
2635
+ const page = pageStart(span);
2636
+ const title = span.sectionId ?? span.formNumber ?? (kind === "table_cell" && span.table?.columnName ? String(span.table.columnName) : void 0) ?? titleCase(kind);
2637
+ const id = nodeId(resolvedDocumentId, kind, [span.id]);
2638
+ nodes.set(id, makeNode({
2639
+ id,
2640
+ documentId: resolvedDocumentId,
2641
+ parentId,
2642
+ kind,
2643
+ title,
2644
+ description: nodeTextDescription({ kind, title, text: span.text, page, formNumber: span.formNumber }),
2645
+ textExcerpt: truncate(span.text, 1600),
2646
+ sourceSpanIds: [span.id],
2647
+ pageStart: page,
2648
+ pageEnd: pageEnd(span),
2649
+ bbox: span.bbox,
2650
+ order: order++,
2651
+ metadata: {
2652
+ ...span.metadata ?? {},
2653
+ formNumber: span.formNumber,
2654
+ sourceUnit: sourceUnit2(span)
2655
+ }
2656
+ }));
2657
+ };
2658
+ for (const span of orderedSpans) {
2659
+ const page = pageStart(span);
2660
+ const pageParentId = page ? ensurePage(page) : rootId;
2661
+ const kind = normalizeNodeKind(span);
2662
+ if (kind === "page") continue;
2663
+ if (kind === "table_row") {
2664
+ const tableParentId = ensureTable(span, pageParentId);
2665
+ const rowKey = span.id;
2666
+ const id = nodeId(resolvedDocumentId, "table_row", [span.id]);
2667
+ nodes.set(id, makeNode({
2668
+ id,
2669
+ documentId: resolvedDocumentId,
2670
+ parentId: tableParentId,
2671
+ kind,
2672
+ title: span.table?.isHeader ? "Header row" : `Row ${(span.table?.rowIndex ?? 0) + 1}`,
2673
+ description: nodeTextDescription({ kind, title: "Table row", text: span.text, page }),
2674
+ textExcerpt: truncate(span.text, 1600),
2675
+ sourceSpanIds: [span.id],
2676
+ pageStart: page,
2677
+ pageEnd: pageEnd(span),
2678
+ bbox: span.bbox,
2679
+ order: order++,
2680
+ metadata: {
2681
+ ...span.metadata ?? {},
2682
+ ...span.table ?? {},
2683
+ sourceUnit: "table_row"
2684
+ }
2685
+ }));
2686
+ rowNodeIds.set(rowKey, id);
2687
+ continue;
2688
+ }
2689
+ if (kind === "table_cell") {
2690
+ const tableParentId = ensureTable(span, pageParentId);
2691
+ const parentRowId2 = rowSpanId(span);
2692
+ const parentId = parentRowId2 ? rowNodeIds.get(parentRowId2) ?? tableParentId : tableParentId;
2693
+ addStandaloneSpanNode(span, parentId);
2694
+ continue;
2695
+ }
2696
+ addStandaloneSpanNode(span, pageParentId);
2697
+ }
2698
+ return normalizeDocumentSourceTreePaths([...nodes.values()]);
2699
+ }
2700
+
2701
+ // src/source/operational-profile.ts
2702
+ function normalizeWhitespace3(value) {
2703
+ return value.replace(/\s+/g, " ").trim();
2704
+ }
2705
+ function cleanValue(value) {
2706
+ if (!value) return void 0;
2707
+ return normalizeWhitespace3(value.replace(/^[\s:;#-]+|[\s;,.]+$/g, ""));
2708
+ }
2709
+ function moneyValue(value) {
2710
+ const clean = cleanValue(value);
2711
+ if (!clean) return void 0;
2712
+ if (/^\$/.test(clean)) return clean;
2713
+ if (/^\d{1,3}(?:,\d{3})*(?:\.\d{2})?$/.test(clean)) return `$${clean}`;
2714
+ return clean;
2715
+ }
2716
+ function nodeText(node) {
2717
+ return normalizeWhitespace3([
2718
+ node.title,
2719
+ node.description,
2720
+ node.textExcerpt
2721
+ ].filter(Boolean).join(" "));
2722
+ }
2723
+ function valueFromNode(node, value, confidence = "medium") {
2724
+ return {
2725
+ value,
2726
+ confidence,
2727
+ sourceNodeIds: [node.id],
2728
+ sourceSpanIds: node.sourceSpanIds
2729
+ };
2730
+ }
2731
+ function firstMatch(nodes, patterns) {
2732
+ for (const node of nodes) {
2733
+ const text = nodeText(node);
2734
+ for (const pattern of patterns) {
2735
+ const match = text.match(pattern);
2736
+ const value = cleanValue(match?.[1]);
2737
+ if (value) return valueFromNode(node, value, "high");
2738
+ }
2739
+ }
2740
+ return void 0;
2741
+ }
2742
+ function inferPolicyTypes(nodes) {
2743
+ const text = nodes.slice(0, 40).map(nodeText).join(" ").toLowerCase();
2744
+ const types = [];
2745
+ const add = (pattern, type) => {
2746
+ if (pattern.test(text) && !types.includes(type)) types.push(type);
2747
+ };
2748
+ add(/\b(cyber|network security|privacy liability|data breach)\b/i, "cyber");
2749
+ add(/\b(professional liability|errors?\s*&?\s*omissions|e&o)\b/i, "professional_liability");
2750
+ add(/\b(commercial general liability|general liability|cgl)\b/i, "general_liability");
2751
+ add(/\b(umbrella|excess liability)\b/i, "umbrella");
2752
+ add(/\b(workers'? compensation|employers'? liability)\b/i, "workers_comp");
2753
+ add(/\b(commercial auto|business auto|automobile liability)\b/i, "commercial_auto");
2754
+ add(/\b(commercial property|property coverage|building coverage)\b/i, "commercial_property");
2755
+ return types.length ? types : ["other"];
2756
+ }
2757
+ function inferDocumentType(nodes) {
2758
+ const text = nodes.slice(0, 25).map(nodeText).join(" ").toLowerCase();
2759
+ if (/\b(quote|proposal|quotation|indication)\b/.test(text) && !/\bpolicy number\b/.test(text)) {
2760
+ return "quote";
2761
+ }
2762
+ return "policy";
2763
+ }
2764
+ function coverageNameFromRow(text) {
2765
+ const labelled = text.match(/\b(?:coverage|coverage part|line)\s*:?\s*([^|;$]{3,80})/i)?.[1];
2766
+ if (labelled) return cleanValue(labelled);
2767
+ const parts = text.split(/\s+\|\s+| {2,}|\t/).map(cleanValue).filter(Boolean);
2768
+ const first = parts.find(
2769
+ (part) => !/^(limit|limits?|deductible|premium|amount|basis|rate|retroactive|aggregate|each occurrence)$/i.test(part) && !/^\$?[\d,]+/.test(part)
2770
+ );
2771
+ return cleanValue(first);
2772
+ }
2773
+ function limitFromText(text) {
2774
+ return moneyValue(
2775
+ 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]
2776
+ );
2777
+ }
2778
+ function deductibleFromText(text) {
2779
+ return moneyValue(text.match(/\b(?:deductible|retention|sir)\s*:?\s*(\$?\d[\d,]*(?:\.\d{2})?)/i)?.[1]);
2780
+ }
2781
+ function premiumFromText(text) {
2782
+ return moneyValue(text.match(/\b(?:premium|total premium|total cost|amount due)\s*:?\s*(\$?\d[\d,]*(?:\.\d{2})?)/i)?.[1]);
2783
+ }
2784
+ function buildCoverages(nodes) {
2785
+ const rows = nodes.filter((node) => {
2786
+ const text = nodeText(node);
2787
+ return (node.kind === "table_row" || node.kind === "schedule" || node.kind === "text") && /\b(coverage|limit|deductible|aggregate|occurrence|liability|premium)\b/i.test(text);
2788
+ });
2789
+ const coverages = [];
2790
+ const seen = /* @__PURE__ */ new Set();
2791
+ for (const row of rows) {
2792
+ const text = nodeText(row);
2793
+ const name = coverageNameFromRow(text);
2794
+ const limit = limitFromText(text);
2795
+ const deductible = deductibleFromText(text);
2796
+ const premium = premiumFromText(text);
2797
+ if (!name || !limit && !deductible && !premium) continue;
2798
+ const key = [name.toLowerCase(), limit, deductible, premium].join("|");
2799
+ if (seen.has(key)) continue;
2800
+ seen.add(key);
2801
+ coverages.push({
2802
+ name,
2803
+ limit,
2804
+ deductible,
2805
+ premium,
2806
+ formNumber: typeof row.metadata?.formNumber === "string" ? row.metadata.formNumber : void 0,
2807
+ sourceNodeIds: [row.id],
2808
+ sourceSpanIds: row.sourceSpanIds
2809
+ });
2810
+ if (coverages.length >= 60) break;
2811
+ }
2812
+ return coverages;
2813
+ }
2814
+ function buildParties(profile) {
2815
+ const parties = [];
2816
+ if (profile.namedInsured) {
2817
+ parties.push({
2818
+ role: "named_insured",
2819
+ name: profile.namedInsured.value,
2820
+ sourceNodeIds: profile.namedInsured.sourceNodeIds,
2821
+ sourceSpanIds: profile.namedInsured.sourceSpanIds
2822
+ });
2823
+ }
2824
+ if (profile.insurer) {
2825
+ parties.push({
2826
+ role: "insurer",
2827
+ name: profile.insurer.value,
2828
+ sourceNodeIds: profile.insurer.sourceNodeIds,
2829
+ sourceSpanIds: profile.insurer.sourceSpanIds
2830
+ });
2831
+ }
2832
+ if (profile.broker) {
2833
+ parties.push({
2834
+ role: "broker",
2835
+ name: profile.broker.value,
2836
+ sourceNodeIds: profile.broker.sourceNodeIds,
2837
+ sourceSpanIds: profile.broker.sourceSpanIds
2838
+ });
2839
+ }
2840
+ return parties;
2841
+ }
2842
+ function buildEndorsementSupport(nodes) {
2843
+ const support = [];
2844
+ for (const node of nodes) {
2845
+ const text = nodeText(node);
2846
+ const add = (kind, status) => {
2847
+ if (support.some((item) => item.kind === kind && item.status === status && item.sourceNodeIds.includes(node.id))) {
2848
+ return;
2849
+ }
2850
+ support.push({
2851
+ kind,
2852
+ status,
2853
+ summary: node.textExcerpt ?? node.description,
2854
+ sourceNodeIds: [node.id],
2855
+ sourceSpanIds: node.sourceSpanIds
2856
+ });
2857
+ };
2858
+ if (/additional insured/i.test(text)) add("additional_insured", /not included|requires endorsement|only by endorsement/i.test(text) ? "requires_review" : "supported");
2859
+ 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");
2860
+ 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");
2861
+ if (/loss payee|mortgagee/i.test(text)) add(/mortgagee/i.test(text) ? "mortgagee" : "loss_payee", "supported");
2862
+ if (support.length >= 20) break;
2863
+ }
2864
+ return support;
2865
+ }
2866
+ function buildDeterministicOperationalProfile(params) {
2867
+ const nodes = params.sourceTree.filter((node) => node.kind !== "document");
2868
+ const partial = {
2869
+ documentType: inferDocumentType(nodes),
2870
+ policyTypes: inferPolicyTypes(nodes),
2871
+ policyNumber: firstMatch(nodes, [
2872
+ /\bpolicy\s*(?:number|no\.?|#)\s*:?\s*([A-Z0-9][A-Z0-9-]{4,})/i,
2873
+ /\bpolicy\s*[:#]\s*([A-Z0-9][A-Z0-9-]{4,})/i
2874
+ ]),
2875
+ namedInsured: firstMatch(nodes, [
2876
+ /\b(?:named insured|insured name|insured)\s*:?\s*(.+?)(?=\s+(?:coverage|policy number|insurer|carrier|premium|effective|expiration)\b|[|;\n]|$)/i,
2877
+ /\b(?:applicant|policyholder)\s*:?\s*(.+?)(?=\s+(?:coverage|policy number|insurer|carrier|premium|effective|expiration)\b|[|;\n]|$)/i
2878
+ ]),
2879
+ insurer: firstMatch(nodes, [
2880
+ /\b(?:insurer|carrier|company|security)\s*:?\s*([^|;\n]{3,120})/i,
2881
+ /\bunderwritten by\s+([^|;\n]{3,120})/i
2882
+ ]),
2883
+ broker: firstMatch(nodes, [
2884
+ /\b(?:broker|producer|agent)\s*:?\s*([^|;\n]{3,120})/i
2885
+ ]),
2886
+ effectiveDate: firstMatch(nodes, [
2887
+ /\b(?:effective date|policy period from|from)\s*:?\s*([0-9]{1,2}[/-][0-9]{1,2}[/-][0-9]{2,4})/i,
2888
+ /\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
2889
+ ]),
2890
+ expirationDate: firstMatch(nodes, [
2891
+ /\b(?:expiration date|expiry date|expires|to)\s*:?\s*([0-9]{1,2}[/-][0-9]{1,2}[/-][0-9]{2,4})/i,
2892
+ /\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
2893
+ ]),
2894
+ retroactiveDate: firstMatch(nodes, [
2895
+ /\bretroactive date\s*:?\s*([0-9]{1,2}[/-][0-9]{1,2}[/-][0-9]{2,4}|full prior acts|none)/i
2896
+ ]),
2897
+ premium: firstMatch(nodes, [
2898
+ /\b(?:total premium|premium|total cost|amount due)\s*:?\s*(\$?\d[\d,]*(?:\.\d{2})?)/i
2899
+ ])
2900
+ };
2901
+ const coverages = buildCoverages(nodes);
2902
+ const coverageTypes = [...new Set(coverages.map((coverage) => coverage.name))];
2903
+ const sourceNodeIds = [.../* @__PURE__ */ new Set([
2904
+ ...Object.values(partial).flatMap(
2905
+ (value) => value && typeof value === "object" && "sourceNodeIds" in value ? value.sourceNodeIds : []
2906
+ ),
2907
+ ...coverages.flatMap((coverage) => coverage.sourceNodeIds)
2908
+ ])];
2909
+ const sourceSpanIds = [.../* @__PURE__ */ new Set([
2910
+ ...Object.values(partial).flatMap(
2911
+ (value) => value && typeof value === "object" && "sourceSpanIds" in value ? value.sourceSpanIds : []
2912
+ ),
2913
+ ...coverages.flatMap((coverage) => coverage.sourceSpanIds)
2914
+ ])];
2915
+ return PolicyOperationalProfileSchema.parse({
2916
+ ...partial,
2917
+ coverageTypes,
2918
+ coverages,
2919
+ parties: buildParties(partial),
2920
+ endorsementSupport: buildEndorsementSupport(nodes),
2921
+ sourceNodeIds,
2922
+ sourceSpanIds,
2923
+ warnings: [
2924
+ ...coverages.length === 0 ? ["No source-backed coverage schedule rows were identified deterministically."] : [],
2925
+ ...!partial.policyNumber ? ["No source-backed policy number was identified deterministically."] : [],
2926
+ ...!partial.namedInsured ? ["No source-backed named insured was identified deterministically."] : []
2927
+ ]
2928
+ });
2929
+ }
2930
+ function mergeOperationalProfile(base, candidate, validNodeIds, validSpanIds) {
2931
+ const keepIds = (ids, valid) => Array.isArray(ids) ? ids.filter((id) => typeof id === "string" && valid.has(id)) : [];
2932
+ const mergeValue = (fallback, next) => {
2933
+ if (!next || typeof next !== "object" || Array.isArray(next)) return fallback;
2934
+ const record = next;
2935
+ const value = typeof record.value === "string" ? cleanValue(record.value) : void 0;
2936
+ if (!value) return fallback;
2937
+ const sourceNodeIds = keepIds(record.sourceNodeIds, validNodeIds);
2938
+ const sourceSpanIds = keepIds(record.sourceSpanIds, validSpanIds);
2939
+ if (sourceNodeIds.length === 0 && sourceSpanIds.length === 0) return fallback;
2940
+ return {
2941
+ value,
2942
+ normalizedValue: typeof record.normalizedValue === "string" ? record.normalizedValue : fallback?.normalizedValue,
2943
+ confidence: record.confidence === "high" || record.confidence === "low" || record.confidence === "medium" ? record.confidence : "medium",
2944
+ sourceNodeIds,
2945
+ sourceSpanIds
2946
+ };
2947
+ };
2948
+ const coverages = Array.isArray(candidate.coverages) ? candidate.coverages.map((coverage) => ({
2949
+ ...coverage,
2950
+ sourceNodeIds: keepIds(coverage.sourceNodeIds, validNodeIds),
2951
+ sourceSpanIds: keepIds(coverage.sourceSpanIds, validSpanIds)
2952
+ })).filter((coverage) => coverage.name && (coverage.sourceNodeIds.length > 0 || coverage.sourceSpanIds.length > 0)) : base.coverages;
2953
+ return PolicyOperationalProfileSchema.parse({
2954
+ ...base,
2955
+ documentType: candidate.documentType === "quote" ? "quote" : candidate.documentType === "policy" ? "policy" : base.documentType,
2956
+ policyTypes: Array.isArray(candidate.policyTypes) && candidate.policyTypes.length > 0 ? candidate.policyTypes : base.policyTypes,
2957
+ policyNumber: mergeValue(base.policyNumber, candidate.policyNumber),
2958
+ namedInsured: mergeValue(base.namedInsured, candidate.namedInsured),
2959
+ insurer: mergeValue(base.insurer, candidate.insurer),
2960
+ broker: mergeValue(base.broker, candidate.broker),
2961
+ effectiveDate: mergeValue(base.effectiveDate, candidate.effectiveDate),
2962
+ expirationDate: mergeValue(base.expirationDate, candidate.expirationDate),
2963
+ retroactiveDate: mergeValue(base.retroactiveDate, candidate.retroactiveDate),
2964
+ premium: mergeValue(base.premium, candidate.premium),
2965
+ coverageTypes: Array.isArray(candidate.coverageTypes) && candidate.coverageTypes.length > 0 ? candidate.coverageTypes : base.coverageTypes,
2966
+ coverages,
2967
+ parties: base.parties,
2968
+ endorsementSupport: base.endorsementSupport,
2969
+ sourceNodeIds: [.../* @__PURE__ */ new Set([...base.sourceNodeIds, ...keepIds(candidate.sourceNodeIds, validNodeIds)])],
2970
+ sourceSpanIds: [.../* @__PURE__ */ new Set([...base.sourceSpanIds, ...keepIds(candidate.sourceSpanIds, validSpanIds)])],
2971
+ warnings: base.warnings
2972
+ });
2973
+ }
2974
+
2357
2975
  // src/extraction/pdf.ts
2358
2976
  import {
2359
2977
  PDFDocument,
@@ -2743,15 +3361,15 @@ function normalizeItem(ref, item) {
2743
3361
  if (!text) return void 0;
2744
3362
  const table = getItemTable(item);
2745
3363
  const pages = (item.prov ?? []).map((prov) => getPageNumber(prov)).filter((page) => typeof page === "number" && page > 0);
2746
- const pageStart = pages.length ? Math.min(...pages) : void 0;
2747
- const pageEnd = pages.length ? Math.max(...pages) : pageStart;
3364
+ const pageStart2 = pages.length ? Math.min(...pages) : void 0;
3365
+ const pageEnd2 = pages.length ? Math.max(...pages) : pageStart2;
2748
3366
  const bboxes = (item.prov ?? []).map((prov) => toSourceSpanBBox(prov)).filter((bbox) => Boolean(bbox));
2749
3367
  return {
2750
3368
  ref,
2751
3369
  label: typeof item.label === "string" ? item.label : void 0,
2752
3370
  text,
2753
- pageStart,
2754
- pageEnd,
3371
+ pageStart: pageStart2,
3372
+ pageEnd: pageEnd2,
2755
3373
  bboxes: bboxes.length ? bboxes : void 0,
2756
3374
  table
2757
3375
  };
@@ -2763,7 +3381,7 @@ function buildSourceSpansForUnit(unit, index, options) {
2763
3381
  doclingRef: unit.ref,
2764
3382
  ...unit.label ? { doclingLabel: unit.label } : {}
2765
3383
  };
2766
- const tableId = unit.table ? `${unit.ref}:table` : void 0;
3384
+ const tableId2 = unit.table ? `${unit.ref}:table` : void 0;
2767
3385
  const tableSpan = withDoclingKind(buildSourceSpan(
2768
3386
  {
2769
3387
  documentId: options.documentId,
@@ -2773,7 +3391,7 @@ function buildSourceSpansForUnit(unit, index, options) {
2773
3391
  pageEnd: unit.pageEnd,
2774
3392
  sectionId: unit.label,
2775
3393
  sourceUnit: unit.table ? "table" : labelToSourceUnit(unit.label),
2776
- table: tableId ? { tableId } : void 0,
3394
+ table: tableId2 ? { tableId: tableId2 } : void 0,
2777
3395
  metadata: baseMetadata
2778
3396
  },
2779
3397
  index * 1e3
@@ -2797,7 +3415,7 @@ function buildSourceSpansForUnit(unit, index, options) {
2797
3415
  sourceUnit: "table_row",
2798
3416
  parentSpanId: tableSpan.id,
2799
3417
  table: {
2800
- tableId,
3418
+ tableId: tableId2,
2801
3419
  tableSpanId: tableSpan.id,
2802
3420
  rowIndex,
2803
3421
  isHeader
@@ -2805,7 +3423,7 @@ function buildSourceSpansForUnit(unit, index, options) {
2805
3423
  metadata: {
2806
3424
  ...baseMetadata,
2807
3425
  sourceUnit: "table_row",
2808
- tableId: tableId ?? "",
3426
+ tableId: tableId2 ?? "",
2809
3427
  tableSpanId: tableSpan.id,
2810
3428
  rowIndex: String(rowIndex),
2811
3429
  isHeader: String(isHeader)
@@ -2829,7 +3447,7 @@ function buildSourceSpansForUnit(unit, index, options) {
2829
3447
  sourceUnit: "table_cell",
2830
3448
  parentSpanId: rowSpan.id,
2831
3449
  table: {
2832
- tableId,
3450
+ tableId: tableId2,
2833
3451
  tableSpanId: tableSpan.id,
2834
3452
  rowSpanId: rowSpan.id,
2835
3453
  rowIndex,
@@ -2840,7 +3458,7 @@ function buildSourceSpansForUnit(unit, index, options) {
2840
3458
  metadata: {
2841
3459
  ...baseMetadata,
2842
3460
  sourceUnit: "table_cell",
2843
- tableId: tableId ?? "",
3461
+ tableId: tableId2 ?? "",
2844
3462
  tableSpanId: tableSpan.id,
2845
3463
  rowSpanId: rowSpan.id,
2846
3464
  rowIndex: String(rowIndex),
@@ -2995,7 +3613,7 @@ function buildSourceContext(spans, maxChars = 12e3) {
2995
3613
  const lines = [];
2996
3614
  let length = 0;
2997
3615
  for (const span of orderSourceSpansForContext(spans)) {
2998
- const unit = sourceUnit2(span);
3616
+ const unit = sourceUnit3(span);
2999
3617
  const table = span.table;
3000
3618
  const tableContext = [
3001
3619
  unit ? ` unit:${unit}` : "",
@@ -3019,11 +3637,11 @@ ${lines.join("\n\n")}
3019
3637
 
3020
3638
  Use sourceSpan IDs when grounding extracted contractual values.`;
3021
3639
  }
3022
- function sourceUnit2(span) {
3640
+ function sourceUnit3(span) {
3023
3641
  return span.sourceUnit ?? span.metadata?.sourceUnit;
3024
3642
  }
3025
3643
  function sourceContextRank(span) {
3026
- switch (sourceUnit2(span)) {
3644
+ switch (sourceUnit3(span)) {
3027
3645
  case "table_row":
3028
3646
  return 0;
3029
3647
  case "table":
@@ -3042,10 +3660,10 @@ function sourceContextRank(span) {
3042
3660
  }
3043
3661
  function orderSourceSpansForContext(spans) {
3044
3662
  const parentRows = new Set(
3045
- spans.filter((span) => sourceUnit2(span) === "table_row").map((span) => span.id)
3663
+ spans.filter((span) => sourceUnit3(span) === "table_row").map((span) => span.id)
3046
3664
  );
3047
3665
  const filtered = spans.filter((span) => {
3048
- if (sourceUnit2(span) !== "table_cell") return true;
3666
+ if (sourceUnit3(span) !== "table_cell") return true;
3049
3667
  const parent = span.parentSpanId ?? span.table?.rowSpanId ?? span.metadata?.rowSpanId;
3050
3668
  return !parent || !parentRows.has(parent);
3051
3669
  });
@@ -3439,7 +4057,7 @@ function stringValue2(record, ...keys) {
3439
4057
  function stringArray(value) {
3440
4058
  return Array.isArray(value) ? value.filter((item) => typeof item === "string" && item.length > 0) : [];
3441
4059
  }
3442
- function sourceUnit3(span) {
4060
+ function sourceUnit4(span) {
3443
4061
  return span.sourceUnit ?? span.metadata?.sourceUnit;
3444
4062
  }
3445
4063
  function spanPage(span) {
@@ -3449,7 +4067,7 @@ function spanPageEnd(span) {
3449
4067
  return span.pageEnd ?? span.location?.endPage ?? spanPage(span);
3450
4068
  }
3451
4069
  function sourceSpansForPage(sourceSpans, page) {
3452
- return sourceSpans.filter((span) => spanPage(span) === page && sourceUnit3(span) === "page").map((span) => span.id);
4070
+ return sourceSpans.filter((span) => spanPage(span) === page && sourceUnit4(span) === "page").map((span) => span.id);
3453
4071
  }
3454
4072
  function nodePageOverlaps(node, record) {
3455
4073
  const recordStart = numberValue(record, "pageNumber", "pageStart", "resolvedFromPage");
@@ -3501,8 +4119,8 @@ function attachDocumentNodeIds(document, nodes) {
3501
4119
  function buildNodeFromSection(section, index) {
3502
4120
  const title = stringValue2(section, "title", "name", "sectionRef") ?? `Section ${index + 1}`;
3503
4121
  const id = stringValue2(section, "documentNodeId", "recordId") ?? `section:${index}:${slugPart(title)}`;
3504
- const pageStart = numberValue(section, "pageStart", "pageNumber");
3505
- const pageEnd = numberValue(section, "pageEnd", "pageNumber") ?? pageStart;
4122
+ const pageStart2 = numberValue(section, "pageStart", "pageNumber");
4123
+ const pageEnd2 = numberValue(section, "pageEnd", "pageNumber") ?? pageStart2;
3506
4124
  const type = stringValue2(section, "type");
3507
4125
  const coverageType = stringValue2(section, "coverageType");
3508
4126
  const sourceSpanIds = stringArray(section.sourceSpanIds);
@@ -3517,8 +4135,8 @@ function buildNodeFromSection(section, index) {
3517
4135
  label: type,
3518
4136
  level: 2,
3519
4137
  sectionNumber: stringValue2(subsection, "sectionNumber"),
3520
- pageStart: numberValue(subsection, "pageNumber") ?? pageStart,
3521
- pageEnd: numberValue(subsection, "pageNumber") ?? pageStart,
4138
+ pageStart: numberValue(subsection, "pageNumber") ?? pageStart2,
4139
+ pageEnd: numberValue(subsection, "pageNumber") ?? pageStart2,
3522
4140
  excerpt: stringValue2(subsection, "excerpt"),
3523
4141
  content: stringValue2(subsection, "content"),
3524
4142
  sourceSpanIds: stringArray(subsection.sourceSpanIds),
@@ -3533,8 +4151,8 @@ function buildNodeFromSection(section, index) {
3533
4151
  label: type,
3534
4152
  level: 1,
3535
4153
  sectionNumber: stringValue2(section, "sectionNumber"),
3536
- pageStart,
3537
- pageEnd,
4154
+ pageStart: pageStart2,
4155
+ pageEnd: pageEnd2,
3538
4156
  formNumber: stringValue2(section, "formNumber"),
3539
4157
  formTitle: stringValue2(section, "formTitle"),
3540
4158
  excerpt: stringValue2(section, "excerpt"),
@@ -3547,17 +4165,17 @@ function buildNodeFromSection(section, index) {
3547
4165
  }
3548
4166
  function buildNodesFromSourceSpans(sourceSpans) {
3549
4167
  const candidates = sourceSpans.filter((span) => {
3550
- const unit = sourceUnit3(span);
4168
+ const unit = sourceUnit4(span);
3551
4169
  return unit === "section" || unit === "section_candidate" || unit === "page";
3552
4170
  });
3553
4171
  return candidates.sort((left, right) => (spanPage(left) ?? 0) - (spanPage(right) ?? 0) || left.id.localeCompare(right.id)).map((span, index) => {
3554
- const title = span.sectionId ?? span.formNumber ?? (sourceUnit3(span) === "page" && spanPage(span) ? `Page ${spanPage(span)}` : `Source unit ${index + 1}`);
4172
+ const title = span.sectionId ?? span.formNumber ?? (sourceUnit4(span) === "page" && spanPage(span) ? `Page ${spanPage(span)}` : `Source unit ${index + 1}`);
3555
4173
  return {
3556
4174
  id: `source:${index}:${slugPart(span.id)}`,
3557
4175
  title,
3558
4176
  originalTitle: title,
3559
- type: sourceUnit3(span),
3560
- label: sourceUnit3(span),
4177
+ type: sourceUnit4(span),
4178
+ label: sourceUnit4(span),
3561
4179
  level: 1,
3562
4180
  pageStart: spanPage(span),
3563
4181
  pageEnd: spanPageEnd(span),
@@ -7309,7 +7927,7 @@ function numberValue2(value) {
7309
7927
  function normalize(value) {
7310
7928
  return value.toLowerCase().replace(/&/g, "and").replace(/[^a-z0-9]+/g, " ").trim();
7311
7929
  }
7312
- function sourceUnit4(span) {
7930
+ function sourceUnit5(span) {
7313
7931
  return span.sourceUnit ?? span.metadata?.sourceUnit;
7314
7932
  }
7315
7933
  function pageNumber(span) {
@@ -7379,7 +7997,7 @@ function firstField(fields, patterns) {
7379
7997
  }
7380
7998
  function coverageFromRow(span) {
7381
7999
  const rowText = span.text.trim();
7382
- if (!rowText || sourceUnit4(span) !== "table_row") return void 0;
8000
+ if (!rowText || sourceUnit5(span) !== "table_row") return void 0;
7383
8001
  if (span.table?.isHeader || span.metadata?.isHeader === "true") return void 0;
7384
8002
  const fields = splitRowFields(rowText);
7385
8003
  let name;
@@ -7459,7 +8077,7 @@ function recoverCoverageScheduleRows(params) {
7459
8077
  const payload = params.memory.get("coverage_limits");
7460
8078
  const existing = Array.isArray(payload?.coverages) ? payload.coverages : [];
7461
8079
  const pages = coveragePages(params.pageAssignments);
7462
- const candidates = params.sourceSpans.filter((span) => sourceUnit4(span) === "table_row").filter((span) => {
8080
+ const candidates = params.sourceSpans.filter((span) => sourceUnit5(span) === "table_row").filter((span) => {
7463
8081
  const page = pageNumber(span);
7464
8082
  return page !== void 0 && pages.has(page);
7465
8083
  }).map(coverageFromRow).filter((coverage) => Boolean(coverage));
@@ -8095,13 +8713,13 @@ function buildPlanFromPageAssignments(pageAssignments, pageCount, formInventory)
8095
8713
  const expanded = new Set(pages);
8096
8714
  for (const page of pages) {
8097
8715
  for (const form of contextualForms) {
8098
- const pageStart = form.pageStart;
8099
- const pageEnd = form.pageEnd ?? form.pageStart;
8716
+ const pageStart2 = form.pageStart;
8717
+ const pageEnd2 = form.pageEnd ?? form.pageStart;
8100
8718
  const formType = form.formType;
8101
8719
  const supportsContextualExpansion = extractorName === "endorsements" ? formType === "endorsement" : formType === "coverage" || formType === "endorsement";
8102
8720
  if (!supportsContextualExpansion) continue;
8103
- if (page < pageStart || page > pageEnd) continue;
8104
- for (let current = pageStart; current <= pageEnd; current += 1) {
8721
+ if (page < pageStart2 || page > pageEnd2) continue;
8722
+ for (let current = pageStart2; current <= pageEnd2; current += 1) {
8105
8723
  expanded.add(current);
8106
8724
  }
8107
8725
  }
@@ -8182,11 +8800,11 @@ function textMatches(record, span) {
8182
8800
  function sourceHashFor(spans) {
8183
8801
  return spans.map((span) => span.textHash ?? span.hash).filter(Boolean).join(":") || void 0;
8184
8802
  }
8185
- function sourceUnit5(span) {
8803
+ function sourceUnit6(span) {
8186
8804
  return span.sourceUnit ?? span.metadata?.sourceUnit;
8187
8805
  }
8188
8806
  function hierarchyScore(span) {
8189
- switch (sourceUnit5(span)) {
8807
+ switch (sourceUnit6(span)) {
8190
8808
  case "table_row":
8191
8809
  return 5;
8192
8810
  case "table":
@@ -8209,8 +8827,8 @@ function preferParentRows(matches, sourceSpans) {
8209
8827
  const expanded = [];
8210
8828
  const seen = /* @__PURE__ */ new Set();
8211
8829
  for (const match of matches) {
8212
- const parent = sourceUnit5(match) === "table_cell" ? byId.get(parentRowId(match) ?? "") : void 0;
8213
- const preferred = parent && sourceUnit5(parent) === "table_row" ? parent : match;
8830
+ const parent = sourceUnit6(match) === "table_cell" ? byId.get(parentRowId(match) ?? "") : void 0;
8831
+ const preferred = parent && sourceUnit6(parent) === "table_row" ? parent : match;
8214
8832
  if (seen.has(preferred.id)) continue;
8215
8833
  seen.add(preferred.id);
8216
8834
  expanded.push(preferred);
@@ -8219,11 +8837,11 @@ function preferParentRows(matches, sourceSpans) {
8219
8837
  }
8220
8838
  function findSourceSpansForRecord(record, sourceSpans) {
8221
8839
  if (sourceSpans.length === 0) return [];
8222
- const pageStart = numberValue3(record, "pageNumber", "pageStart");
8223
- const pageEnd = numberValue3(record, "pageNumber", "pageEnd");
8840
+ const pageStart2 = numberValue3(record, "pageNumber", "pageStart");
8841
+ const pageEnd2 = numberValue3(record, "pageNumber", "pageEnd");
8224
8842
  const scored = sourceSpans.map((span) => {
8225
8843
  let score = 0;
8226
- if (pageOverlaps(pageStart, pageEnd, span)) score += 4;
8844
+ if (pageOverlaps(pageStart2, pageEnd2, span)) score += 4;
8227
8845
  if (formMatches(record, span)) score += 3;
8228
8846
  if (textMatches(record, span)) score += 2;
8229
8847
  if (score > 0) score += hierarchyScore(span);
@@ -8262,6 +8880,422 @@ function groundExtractionMemoryWithSourceSpans(memory, sourceSpans) {
8262
8880
  }
8263
8881
  }
8264
8882
 
8883
+ // src/extraction/source-tree-extractor.ts
8884
+ import { z as z41 } from "zod";
8885
+ var ORGANIZABLE_KINDS = [
8886
+ "page_group",
8887
+ "form",
8888
+ "endorsement",
8889
+ "section",
8890
+ "schedule",
8891
+ "clause"
8892
+ ];
8893
+ var SourceTreeOrganizationSchema = z41.object({
8894
+ labels: z41.array(z41.object({
8895
+ nodeId: z41.string(),
8896
+ kind: z41.enum([
8897
+ "document",
8898
+ "page_group",
8899
+ "page",
8900
+ "form",
8901
+ "endorsement",
8902
+ "section",
8903
+ "schedule",
8904
+ "clause",
8905
+ "table",
8906
+ "table_row",
8907
+ "table_cell",
8908
+ "text"
8909
+ ]).optional(),
8910
+ title: z41.string().optional(),
8911
+ description: z41.string().optional()
8912
+ })).default([]),
8913
+ groups: z41.array(z41.object({
8914
+ kind: z41.enum(ORGANIZABLE_KINDS),
8915
+ title: z41.string(),
8916
+ description: z41.string().optional(),
8917
+ childNodeIds: z41.array(z41.string()).min(1)
8918
+ })).default([])
8919
+ });
8920
+ var SourceBackedValueForPromptSchema = z41.object({
8921
+ value: z41.string(),
8922
+ normalizedValue: z41.string().optional(),
8923
+ confidence: z41.enum(["low", "medium", "high"]).optional(),
8924
+ sourceNodeIds: z41.array(z41.string()).default([]),
8925
+ sourceSpanIds: z41.array(z41.string()).default([])
8926
+ });
8927
+ var OperationalProfilePromptSchema = z41.object({
8928
+ documentType: z41.enum(["policy", "quote"]).optional(),
8929
+ policyTypes: z41.array(z41.string()).optional(),
8930
+ policyNumber: SourceBackedValueForPromptSchema.optional(),
8931
+ namedInsured: SourceBackedValueForPromptSchema.optional(),
8932
+ insurer: SourceBackedValueForPromptSchema.optional(),
8933
+ broker: SourceBackedValueForPromptSchema.optional(),
8934
+ effectiveDate: SourceBackedValueForPromptSchema.optional(),
8935
+ expirationDate: SourceBackedValueForPromptSchema.optional(),
8936
+ retroactiveDate: SourceBackedValueForPromptSchema.optional(),
8937
+ premium: SourceBackedValueForPromptSchema.optional(),
8938
+ coverageTypes: z41.array(z41.string()).optional(),
8939
+ coverages: z41.array(z41.object({
8940
+ name: z41.string(),
8941
+ coverageCode: z41.string().optional(),
8942
+ limit: z41.string().optional(),
8943
+ deductible: z41.string().optional(),
8944
+ premium: z41.string().optional(),
8945
+ formNumber: z41.string().optional(),
8946
+ sectionRef: z41.string().optional(),
8947
+ sourceNodeIds: z41.array(z41.string()).default([]),
8948
+ sourceSpanIds: z41.array(z41.string()).default([])
8949
+ })).optional(),
8950
+ sourceNodeIds: z41.array(z41.string()).optional(),
8951
+ sourceSpanIds: z41.array(z41.string()).optional()
8952
+ });
8953
+ function cleanText(value, fallback) {
8954
+ const text = value?.replace(/\s+/g, " ").trim();
8955
+ return text || fallback;
8956
+ }
8957
+ function compactNode(node) {
8958
+ return {
8959
+ id: node.id,
8960
+ kind: node.kind,
8961
+ title: node.title,
8962
+ path: node.path,
8963
+ pageStart: node.pageStart,
8964
+ pageEnd: node.pageEnd,
8965
+ sourceSpanIds: node.sourceSpanIds.slice(0, 8),
8966
+ text: (node.textExcerpt ?? node.description).slice(0, 700)
8967
+ };
8968
+ }
8969
+ function buildOrganizationPrompt(sourceTree) {
8970
+ const nodes = sourceTree.filter((node) => node.kind !== "table_cell").slice(0, 180).map(compactNode);
8971
+ return `You organize an insurance document source tree.
8972
+
8973
+ Rules:
8974
+ - Use only node IDs from the provided list.
8975
+ - Do not invent text, page numbers, source spans, limits, or policy facts.
8976
+ - 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.
8977
+ - Groups must list existing childNodeIds only.
8978
+ - Keep descriptions short and useful for search.
8979
+
8980
+ Source nodes:
8981
+ ${JSON.stringify(nodes, null, 2)}
8982
+
8983
+ Return JSON with labels and groups only.`;
8984
+ }
8985
+ function buildOperationalProfilePrompt(sourceTree, fallback) {
8986
+ const nodes = sourceTree.filter((node) => node.kind !== "document").slice(0, 240).map(compactNode);
8987
+ return `Extract a source-backed operational profile for an insurance policy or quote.
8988
+
8989
+ Return only high-value operational facts needed for policy lists, Q&A, compliance, and certificate generation:
8990
+ - policy number, named insured, insurer/carrier/security, broker/producer, policy period, retroactive date, premium
8991
+ - coverage lines with limits, deductibles, premiums, and form references
8992
+ - coverage type labels
8993
+
8994
+ Rules:
8995
+ - Every returned value must include sourceNodeIds or sourceSpanIds from the provided nodes.
8996
+ - If a value is not directly supported, omit it.
8997
+ - Prefer declarations, schedules, premium tables, and endorsement schedules over generic policy wording.
8998
+ - Do not copy entire policy wording into fields.
8999
+
9000
+ Deterministic baseline:
9001
+ ${JSON.stringify(fallback, null, 2)}
9002
+
9003
+ Source nodes:
9004
+ ${JSON.stringify(nodes, null, 2)}
9005
+
9006
+ Return JSON for the operational profile.`;
9007
+ }
9008
+ function groupNodeId(documentId, group) {
9009
+ return [
9010
+ documentId.replace(/[^a-zA-Z0-9_.:-]/g, "_"),
9011
+ "source_node",
9012
+ group.kind,
9013
+ group.childNodeIds.join("_").replace(/[^a-zA-Z0-9_.:-]/g, "_").slice(0, 80)
9014
+ ].join(":");
9015
+ }
9016
+ function applyOrganization(sourceTree, organization) {
9017
+ const byId = new Map(sourceTree.map((node) => [node.id, node]));
9018
+ let nextTree = sourceTree.map((node) => {
9019
+ const label = organization.labels.find((item) => item.nodeId === node.id);
9020
+ if (!label) return node;
9021
+ return {
9022
+ ...node,
9023
+ kind: label.kind ?? node.kind,
9024
+ title: cleanText(label.title, node.title),
9025
+ description: cleanText(label.description, node.description)
9026
+ };
9027
+ });
9028
+ for (const group of organization.groups.slice(0, 40)) {
9029
+ const children = group.childNodeIds.map((id2) => byId.get(id2)).filter((node2) => Boolean(node2));
9030
+ if (children.length === 0) continue;
9031
+ const parentId = children[0].parentId;
9032
+ if (!children.every((child) => child.parentId === parentId)) continue;
9033
+ const documentId = children[0].documentId;
9034
+ const id = groupNodeId(documentId, group);
9035
+ if (byId.has(id)) continue;
9036
+ const sourceSpanIds = [...new Set(children.flatMap((child) => child.sourceSpanIds))];
9037
+ const pageStarts = children.map((child) => child.pageStart).filter((page) => typeof page === "number");
9038
+ const pageEnds = children.map((child) => child.pageEnd ?? child.pageStart).filter((page) => typeof page === "number");
9039
+ const order = Math.min(...children.map((child) => child.order));
9040
+ const node = {
9041
+ id,
9042
+ documentId,
9043
+ parentId,
9044
+ kind: group.kind,
9045
+ title: group.title,
9046
+ description: group.description ?? group.title,
9047
+ textExcerpt: children.map((child) => child.textExcerpt ?? child.description).filter(Boolean).join("\n\n").slice(0, 1600),
9048
+ sourceSpanIds,
9049
+ pageStart: pageStarts.length ? Math.min(...pageStarts) : void 0,
9050
+ pageEnd: pageEnds.length ? Math.max(...pageEnds) : void 0,
9051
+ bbox: children.flatMap((child) => child.bbox ?? []).slice(0, 12),
9052
+ order,
9053
+ path: "",
9054
+ metadata: { sourceTreeVersion: "v3", organizer: "llm_group" }
9055
+ };
9056
+ nextTree = [
9057
+ ...nextTree.map(
9058
+ (child) => group.childNodeIds.includes(child.id) ? { ...child, parentId: id, order: child.order + 1e-3 } : child
9059
+ ),
9060
+ node
9061
+ ];
9062
+ byId.set(id, node);
9063
+ }
9064
+ return normalizeDocumentSourceTreePaths(nextTree);
9065
+ }
9066
+ function sourceTreeToOutline(sourceTree) {
9067
+ const byParent = /* @__PURE__ */ new Map();
9068
+ for (const node of sourceTree.filter((item) => item.kind !== "document")) {
9069
+ const group = byParent.get(node.parentId) ?? [];
9070
+ group.push(node);
9071
+ byParent.set(node.parentId, group);
9072
+ }
9073
+ const root = sourceTree.find((node) => node.kind === "document");
9074
+ const visit = (node) => ({
9075
+ id: node.id,
9076
+ title: node.title,
9077
+ type: node.kind,
9078
+ label: node.kind,
9079
+ pageStart: node.pageStart,
9080
+ pageEnd: node.pageEnd,
9081
+ excerpt: node.textExcerpt,
9082
+ content: node.textExcerpt,
9083
+ sourceSpanIds: node.sourceSpanIds,
9084
+ sourceTextHash: node.sourceSpanIds.join(":") || void 0,
9085
+ interpretationLabels: [node.kind],
9086
+ children: (byParent.get(node.id) ?? []).map(visit)
9087
+ });
9088
+ return (byParent.get(root?.id) ?? []).map(visit);
9089
+ }
9090
+ function valueOf(profile, key) {
9091
+ const value = profile[key];
9092
+ return value && typeof value === "object" && !Array.isArray(value) && "value" in value ? String(value.value) : void 0;
9093
+ }
9094
+ function materializeDocument(params) {
9095
+ const profile = params.operationalProfile;
9096
+ const policyNumber = valueOf(profile, "policyNumber") ?? "Unknown";
9097
+ const insuredName = valueOf(profile, "namedInsured") ?? "Unknown";
9098
+ const carrier = valueOf(profile, "insurer") ?? "Unknown";
9099
+ const effectiveDate = valueOf(profile, "effectiveDate") ?? "Unknown";
9100
+ const expirationDate = valueOf(profile, "expirationDate") ?? "Unknown";
9101
+ const premium = valueOf(profile, "premium");
9102
+ const coverages = profile.coverages.map((coverage) => ({
9103
+ name: coverage.name,
9104
+ coverageCode: coverage.coverageCode,
9105
+ limit: coverage.limit,
9106
+ deductible: coverage.deductible,
9107
+ premium: coverage.premium,
9108
+ formNumber: coverage.formNumber,
9109
+ sectionRef: coverage.sectionRef,
9110
+ sourceSpanIds: coverage.sourceSpanIds,
9111
+ documentNodeId: coverage.sourceNodeIds[0],
9112
+ originalContent: [coverage.name, coverage.limit, coverage.deductible, coverage.premium].filter(Boolean).join(" | ")
9113
+ }));
9114
+ const documentOutline = sourceTreeToOutline(params.sourceTree);
9115
+ const documentMetadata = {
9116
+ sourceTreeVersion: "v3",
9117
+ sourceTreeCanonical: true,
9118
+ tableOfContents: documentOutline.map((node) => ({
9119
+ title: node.title,
9120
+ pageStart: node.pageStart,
9121
+ pageEnd: node.pageEnd,
9122
+ documentNodeId: node.id,
9123
+ sourceSpanIds: node.sourceSpanIds
9124
+ })),
9125
+ agentGuidance: [
9126
+ {
9127
+ kind: "source_tree",
9128
+ title: "Use the source tree as canonical evidence",
9129
+ detail: "Operational fields are projections from source nodes and source spans. Use source nodes for policy wording and exact provenance."
9130
+ }
9131
+ ]
9132
+ };
9133
+ const summary = [
9134
+ carrier !== "Unknown" ? carrier : void 0,
9135
+ policyNumber !== "Unknown" ? `#${policyNumber}` : void 0,
9136
+ insuredName !== "Unknown" ? `for ${insuredName}` : void 0,
9137
+ profile.coverageTypes.length ? `covering ${profile.coverageTypes.slice(0, 5).join(", ")}` : void 0
9138
+ ].filter(Boolean).join(" ");
9139
+ const base = {
9140
+ id: params.id,
9141
+ type: profile.documentType,
9142
+ carrier,
9143
+ security: carrier,
9144
+ insuredName,
9145
+ premium,
9146
+ policyTypes: profile.policyTypes,
9147
+ coverages,
9148
+ documentMetadata,
9149
+ documentOutline,
9150
+ declarations: {
9151
+ fields: [
9152
+ profile.policyNumber ? { field: "policyNumber", value: profile.policyNumber.value, sourceSpanIds: profile.policyNumber.sourceSpanIds } : void 0,
9153
+ profile.namedInsured ? { field: "namedInsured", value: profile.namedInsured.value, sourceSpanIds: profile.namedInsured.sourceSpanIds } : void 0,
9154
+ profile.insurer ? { field: "insurer", value: profile.insurer.value, sourceSpanIds: profile.insurer.sourceSpanIds } : void 0,
9155
+ profile.effectiveDate ? { field: "policyPeriodStart", value: profile.effectiveDate.value, sourceSpanIds: profile.effectiveDate.sourceSpanIds } : void 0,
9156
+ profile.expirationDate ? { field: "policyPeriodEnd", value: profile.expirationDate.value, sourceSpanIds: profile.expirationDate.sourceSpanIds } : void 0
9157
+ ].filter(Boolean)
9158
+ },
9159
+ supplementaryFacts: profile.endorsementSupport.map((item) => ({
9160
+ key: item.kind,
9161
+ value: item.summary,
9162
+ sourceSpanIds: item.sourceSpanIds,
9163
+ documentNodeId: item.sourceNodeIds[0]
9164
+ })),
9165
+ summary: summary || void 0
9166
+ };
9167
+ if (profile.documentType === "quote") {
9168
+ return {
9169
+ ...base,
9170
+ type: "quote",
9171
+ quoteNumber: policyNumber,
9172
+ proposedEffectiveDate: effectiveDate === "Unknown" ? void 0 : effectiveDate,
9173
+ proposedExpirationDate: expirationDate === "Unknown" ? void 0 : expirationDate
9174
+ };
9175
+ }
9176
+ return {
9177
+ ...base,
9178
+ type: "policy",
9179
+ policyNumber,
9180
+ effectiveDate,
9181
+ expirationDate,
9182
+ retroactiveDate: valueOf(profile, "retroactiveDate")
9183
+ };
9184
+ }
9185
+ async function runSourceTreeExtraction(params) {
9186
+ let sourceTree = buildDocumentSourceTree(params.sourceSpans, params.id);
9187
+ const warnings = [];
9188
+ let modelCalls = 0;
9189
+ let callsWithUsage = 0;
9190
+ let callsMissingUsage = 0;
9191
+ const tokenUsage = { inputTokens: 0, outputTokens: 0 };
9192
+ const performanceReport = { modelCalls: [], totalModelCallDurationMs: 0 };
9193
+ const localTrack = (usage, report) => {
9194
+ modelCalls += 1;
9195
+ if (usage) {
9196
+ callsWithUsage += 1;
9197
+ tokenUsage.inputTokens += usage.inputTokens;
9198
+ tokenUsage.outputTokens += usage.outputTokens;
9199
+ } else {
9200
+ callsMissingUsage += 1;
9201
+ }
9202
+ if (report) {
9203
+ performanceReport.modelCalls.push({ ...report, usage, usageReported: !!usage });
9204
+ if (report.durationMs != null) performanceReport.totalModelCallDurationMs += report.durationMs;
9205
+ }
9206
+ params.trackUsage(usage, report);
9207
+ };
9208
+ try {
9209
+ const budget = params.resolveBudget("extraction_source_tree", 4096);
9210
+ const startedAt = Date.now();
9211
+ const response = await safeGenerateObject(
9212
+ params.generateObject,
9213
+ {
9214
+ prompt: buildOrganizationPrompt(sourceTree),
9215
+ schema: SourceTreeOrganizationSchema,
9216
+ maxTokens: budget.maxTokens,
9217
+ taskKind: "extraction_source_tree",
9218
+ budgetDiagnostics: budget,
9219
+ providerOptions: { ...params.providerOptions, sourceSpans: params.sourceSpans }
9220
+ },
9221
+ {
9222
+ fallback: { labels: [], groups: [] },
9223
+ log: params.log
9224
+ }
9225
+ );
9226
+ localTrack(response.usage, {
9227
+ taskKind: "extraction_source_tree",
9228
+ label: "source_tree_organizer",
9229
+ maxTokens: budget.maxTokens,
9230
+ durationMs: Date.now() - startedAt
9231
+ });
9232
+ sourceTree = applyOrganization(sourceTree, response.object);
9233
+ } catch (error) {
9234
+ warnings.push(`Source-tree organizer failed; deterministic tree used (${error instanceof Error ? error.message : String(error)})`);
9235
+ }
9236
+ const deterministicProfile = buildDeterministicOperationalProfile({
9237
+ sourceTree,
9238
+ sourceSpans: params.sourceSpans
9239
+ });
9240
+ let operationalProfile = deterministicProfile;
9241
+ try {
9242
+ const validNodeIds = new Set(sourceTree.map((node) => node.id));
9243
+ const validSpanIds = new Set(params.sourceSpans.map((span) => span.id));
9244
+ const budget = params.resolveBudget("extraction_operational_profile", 8192);
9245
+ const startedAt = Date.now();
9246
+ const response = await safeGenerateObject(
9247
+ params.generateObject,
9248
+ {
9249
+ prompt: buildOperationalProfilePrompt(sourceTree, deterministicProfile),
9250
+ schema: OperationalProfilePromptSchema,
9251
+ maxTokens: budget.maxTokens,
9252
+ taskKind: "extraction_operational_profile",
9253
+ budgetDiagnostics: budget,
9254
+ providerOptions: { ...params.providerOptions, sourceSpans: params.sourceSpans, sourceTree }
9255
+ },
9256
+ {
9257
+ fallback: deterministicProfile,
9258
+ log: params.log
9259
+ }
9260
+ );
9261
+ localTrack(response.usage, {
9262
+ taskKind: "extraction_operational_profile",
9263
+ label: "operational_profile",
9264
+ maxTokens: budget.maxTokens,
9265
+ durationMs: Date.now() - startedAt
9266
+ });
9267
+ operationalProfile = mergeOperationalProfile(
9268
+ deterministicProfile,
9269
+ response.object,
9270
+ validNodeIds,
9271
+ validSpanIds
9272
+ );
9273
+ } catch (error) {
9274
+ warnings.push(`Operational profile model pass failed; deterministic profile used (${error instanceof Error ? error.message : String(error)})`);
9275
+ }
9276
+ const document = materializeDocument({
9277
+ id: params.id,
9278
+ sourceTree,
9279
+ operationalProfile
9280
+ });
9281
+ return {
9282
+ sourceTree,
9283
+ sourceSpans: params.sourceSpans,
9284
+ sourceChunks: chunkSourceSpans(params.sourceSpans),
9285
+ operationalProfile,
9286
+ document,
9287
+ chunks: [],
9288
+ warnings: [...warnings, ...operationalProfile.warnings],
9289
+ tokenUsage,
9290
+ usageReporting: {
9291
+ modelCalls,
9292
+ callsWithUsage,
9293
+ callsMissingUsage
9294
+ },
9295
+ performanceReport
9296
+ };
9297
+ }
9298
+
8265
9299
  // src/extraction/coordinator.ts
8266
9300
  function createExtractor(config) {
8267
9301
  const {
@@ -8447,17 +9481,17 @@ ${span.text}` : span.text;
8447
9481
  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);
8448
9482
  return {
8449
9483
  sections: candidateSpans.map((span, index) => {
8450
- const pageStart = span.pageStart ?? span.location?.startPage ?? span.location?.page ?? startPage;
8451
- const pageEnd = span.pageEnd ?? span.location?.endPage ?? pageStart;
8452
- const title = span.sectionId ?? span.formNumber ?? firstHeadingLine(span.text) ?? `Policy text page ${pageStart}`;
9484
+ const pageStart2 = span.pageStart ?? span.location?.startPage ?? span.location?.page ?? startPage;
9485
+ const pageEnd2 = span.pageEnd ?? span.location?.endPage ?? pageStart2;
9486
+ const title = span.sectionId ?? span.formNumber ?? firstHeadingLine(span.text) ?? `Policy text page ${pageStart2}`;
8453
9487
  return {
8454
9488
  title,
8455
9489
  sectionNumber: span.formNumber,
8456
- pageStart,
8457
- pageEnd,
9490
+ pageStart: pageStart2,
9491
+ pageEnd: pageEnd2,
8458
9492
  type: inferSectionType(title, span.text),
8459
9493
  excerpt: span.text.slice(0, 240),
8460
- recordId: `section_index_${pageStart}_${index}`,
9494
+ recordId: `section_index_${pageStart2}_${index}`,
8461
9495
  sourceSpanIds: [span.id],
8462
9496
  sourceTextHash: span.textHash ?? sourceSpanTextHash(span.text)
8463
9497
  };
@@ -8596,6 +9630,51 @@ ${span.text}` : span.text;
8596
9630
  await sourceStore.addSourceChunks(sourceChunks);
8597
9631
  }
8598
9632
  }
9633
+ if (sourceSpans.length > 0) {
9634
+ onProgress?.("Building source-native document tree...");
9635
+ const v3 = await runSourceTreeExtraction({
9636
+ id,
9637
+ sourceSpans,
9638
+ generateObject,
9639
+ providerOptions: activeProviderOptions,
9640
+ resolveBudget,
9641
+ trackUsage,
9642
+ log
9643
+ });
9644
+ const reviewReport2 = {
9645
+ issues: v3.warnings.map((warning) => ({
9646
+ code: "source_tree_warning",
9647
+ severity: "warning",
9648
+ message: warning
9649
+ })),
9650
+ rounds: [],
9651
+ artifacts: [
9652
+ { kind: "source_tree", label: "Source Tree", itemCount: v3.sourceTree.length },
9653
+ { kind: "source_spans", label: "Source Spans", itemCount: v3.sourceSpans.length },
9654
+ { kind: "operational_profile", label: "Operational Profile", itemCount: v3.operationalProfile.coverages.length }
9655
+ ],
9656
+ reviewRoundRecords: [],
9657
+ formInventory: [],
9658
+ qualityGateStatus: v3.warnings.length > 0 ? "warning" : "passed"
9659
+ };
9660
+ if (shouldFailQualityGate(qualityGate, reviewReport2.qualityGateStatus)) {
9661
+ throw new Error("Extraction quality gate failed. See reviewReport for blocking issues.");
9662
+ }
9663
+ onProgress?.("Source-tree extraction complete.");
9664
+ return {
9665
+ document: v3.document,
9666
+ chunks: [],
9667
+ sourceSpans: v3.sourceSpans,
9668
+ sourceChunks: v3.sourceChunks,
9669
+ sourceTree: v3.sourceTree,
9670
+ operationalProfile: v3.operationalProfile,
9671
+ warnings: v3.warnings,
9672
+ tokenUsage: v3.tokenUsage,
9673
+ usageReporting: v3.usageReporting,
9674
+ performanceReport: v3.performanceReport,
9675
+ reviewReport: reviewReport2
9676
+ };
9677
+ }
8599
9678
  const pipelineCtx = createPipelineContext({
8600
9679
  id,
8601
9680
  onSave: onCheckpointSave,
@@ -9466,8 +10545,8 @@ Respond with JSON only:
9466
10545
  }`;
9467
10546
 
9468
10547
  // src/schemas/application.ts
9469
- import { z as z41 } from "zod";
9470
- var FieldTypeSchema = z41.enum([
10548
+ import { z as z42 } from "zod";
10549
+ var FieldTypeSchema = z42.enum([
9471
10550
  "text",
9472
10551
  "numeric",
9473
10552
  "currency",
@@ -9476,138 +10555,138 @@ var FieldTypeSchema = z41.enum([
9476
10555
  "table",
9477
10556
  "declaration"
9478
10557
  ]);
9479
- var ApplicationFieldSchema = z41.object({
9480
- id: z41.string(),
9481
- label: z41.string(),
9482
- section: z41.string(),
10558
+ var ApplicationFieldSchema = z42.object({
10559
+ id: z42.string(),
10560
+ label: z42.string(),
10561
+ section: z42.string(),
9483
10562
  fieldType: FieldTypeSchema,
9484
- required: z41.boolean(),
9485
- options: z41.array(z41.string()).optional(),
9486
- columns: z41.array(z41.string()).optional(),
9487
- requiresExplanationIfYes: z41.boolean().optional(),
9488
- condition: z41.object({
9489
- dependsOn: z41.string(),
9490
- whenValue: z41.string()
10563
+ required: z42.boolean(),
10564
+ options: z42.array(z42.string()).optional(),
10565
+ columns: z42.array(z42.string()).optional(),
10566
+ requiresExplanationIfYes: z42.boolean().optional(),
10567
+ condition: z42.object({
10568
+ dependsOn: z42.string(),
10569
+ whenValue: z42.string()
9491
10570
  }).optional(),
9492
- value: z41.string().optional(),
9493
- source: z41.string().optional().describe("Where the value came from: auto-fill, user, lookup"),
9494
- confidence: z41.enum(["confirmed", "high", "medium", "low"]).optional(),
9495
- sourceSpanIds: z41.array(z41.string()).optional().describe("Stable source spans that support the field value or field anchor"),
9496
- userSourceSpanIds: z41.array(z41.string()).optional().describe("Message or attachment spans that support user-provided values"),
9497
- pageNumber: z41.number().int().positive().optional().describe("Application page where the field label or anchor appears"),
9498
- fieldAnchorId: z41.string().optional().describe("Stable field anchor ID derived from page, section, label, and form metadata"),
9499
- acroFormName: z41.string().optional().describe("Native PDF AcroForm field name when available"),
9500
- validationStatus: z41.enum(["valid", "needs_review", "unsupported", "missing"]).optional()
9501
- });
9502
- var ApplicationClassifyResultSchema = z41.object({
9503
- isApplication: z41.boolean(),
9504
- confidence: z41.number().min(0).max(1),
9505
- applicationType: z41.string().nullable()
9506
- });
9507
- var FieldExtractionResultSchema = z41.object({
9508
- fields: z41.array(ApplicationFieldSchema)
9509
- });
9510
- var AutoFillMatchSchema = z41.object({
9511
- fieldId: z41.string(),
9512
- value: z41.string(),
9513
- confidence: z41.enum(["confirmed"]),
9514
- contextKey: z41.string()
9515
- });
9516
- var AutoFillResultSchema = z41.object({
9517
- matches: z41.array(AutoFillMatchSchema)
9518
- });
9519
- var QuestionBatchResultSchema = z41.object({
9520
- batches: z41.array(z41.array(z41.string()).describe("Array of field IDs in this batch"))
9521
- });
9522
- var LookupRequestSchema = z41.object({
9523
- type: z41.string().describe("Type of lookup: 'records', 'website', 'policy'"),
9524
- description: z41.string(),
9525
- url: z41.string().optional(),
9526
- targetFieldIds: z41.array(z41.string())
9527
- });
9528
- var ReplyIntentSchema = z41.object({
9529
- primaryIntent: z41.enum(["answers_only", "question", "lookup_request", "mixed"]),
9530
- hasAnswers: z41.boolean(),
9531
- questionText: z41.string().optional(),
9532
- questionFieldIds: z41.array(z41.string()).optional(),
9533
- lookupRequests: z41.array(LookupRequestSchema).optional()
9534
- });
9535
- var ParsedAnswerSchema = z41.object({
9536
- fieldId: z41.string(),
9537
- value: z41.string(),
9538
- explanation: z41.string().optional()
9539
- });
9540
- var AnswerParsingResultSchema = z41.object({
9541
- answers: z41.array(ParsedAnswerSchema),
9542
- unanswered: z41.array(z41.string()).describe("Field IDs that were not answered")
9543
- });
9544
- var LookupFillSchema = z41.object({
9545
- fieldId: z41.string(),
9546
- value: z41.string(),
9547
- source: z41.string().describe("Specific citable reference, e.g. 'GL Policy #POL-12345 (Hartford)'"),
9548
- sourceSpanIds: z41.array(z41.string()).optional()
9549
- });
9550
- var LookupFillResultSchema = z41.object({
9551
- fills: z41.array(LookupFillSchema),
9552
- unfillable: z41.array(z41.string()),
9553
- explanation: z41.string().optional()
9554
- });
9555
- var FlatPdfPlacementSchema = z41.object({
9556
- fieldId: z41.string(),
9557
- page: z41.number(),
9558
- x: z41.number().describe("Percentage from left edge (0-100)"),
9559
- y: z41.number().describe("Percentage from top edge (0-100)"),
9560
- text: z41.string(),
9561
- fontSize: z41.number().optional(),
9562
- isCheckmark: z41.boolean().optional()
9563
- });
9564
- var AcroFormMappingSchema = z41.object({
9565
- fieldId: z41.string(),
9566
- acroFormName: z41.string(),
9567
- value: z41.string()
9568
- });
9569
- var QualityGateStatusSchema = z41.enum(["passed", "warning", "failed"]);
9570
- var QualitySeveritySchema = z41.enum(["info", "warning", "blocking"]);
9571
- var ApplicationQualityIssueSchema = z41.object({
9572
- code: z41.string(),
10571
+ value: z42.string().optional(),
10572
+ source: z42.string().optional().describe("Where the value came from: auto-fill, user, lookup"),
10573
+ confidence: z42.enum(["confirmed", "high", "medium", "low"]).optional(),
10574
+ sourceSpanIds: z42.array(z42.string()).optional().describe("Stable source spans that support the field value or field anchor"),
10575
+ userSourceSpanIds: z42.array(z42.string()).optional().describe("Message or attachment spans that support user-provided values"),
10576
+ pageNumber: z42.number().int().positive().optional().describe("Application page where the field label or anchor appears"),
10577
+ fieldAnchorId: z42.string().optional().describe("Stable field anchor ID derived from page, section, label, and form metadata"),
10578
+ acroFormName: z42.string().optional().describe("Native PDF AcroForm field name when available"),
10579
+ validationStatus: z42.enum(["valid", "needs_review", "unsupported", "missing"]).optional()
10580
+ });
10581
+ var ApplicationClassifyResultSchema = z42.object({
10582
+ isApplication: z42.boolean(),
10583
+ confidence: z42.number().min(0).max(1),
10584
+ applicationType: z42.string().nullable()
10585
+ });
10586
+ var FieldExtractionResultSchema = z42.object({
10587
+ fields: z42.array(ApplicationFieldSchema)
10588
+ });
10589
+ var AutoFillMatchSchema = z42.object({
10590
+ fieldId: z42.string(),
10591
+ value: z42.string(),
10592
+ confidence: z42.enum(["confirmed"]),
10593
+ contextKey: z42.string()
10594
+ });
10595
+ var AutoFillResultSchema = z42.object({
10596
+ matches: z42.array(AutoFillMatchSchema)
10597
+ });
10598
+ var QuestionBatchResultSchema = z42.object({
10599
+ batches: z42.array(z42.array(z42.string()).describe("Array of field IDs in this batch"))
10600
+ });
10601
+ var LookupRequestSchema = z42.object({
10602
+ type: z42.string().describe("Type of lookup: 'records', 'website', 'policy'"),
10603
+ description: z42.string(),
10604
+ url: z42.string().optional(),
10605
+ targetFieldIds: z42.array(z42.string())
10606
+ });
10607
+ var ReplyIntentSchema = z42.object({
10608
+ primaryIntent: z42.enum(["answers_only", "question", "lookup_request", "mixed"]),
10609
+ hasAnswers: z42.boolean(),
10610
+ questionText: z42.string().optional(),
10611
+ questionFieldIds: z42.array(z42.string()).optional(),
10612
+ lookupRequests: z42.array(LookupRequestSchema).optional()
10613
+ });
10614
+ var ParsedAnswerSchema = z42.object({
10615
+ fieldId: z42.string(),
10616
+ value: z42.string(),
10617
+ explanation: z42.string().optional()
10618
+ });
10619
+ var AnswerParsingResultSchema = z42.object({
10620
+ answers: z42.array(ParsedAnswerSchema),
10621
+ unanswered: z42.array(z42.string()).describe("Field IDs that were not answered")
10622
+ });
10623
+ var LookupFillSchema = z42.object({
10624
+ fieldId: z42.string(),
10625
+ value: z42.string(),
10626
+ source: z42.string().describe("Specific citable reference, e.g. 'GL Policy #POL-12345 (Hartford)'"),
10627
+ sourceSpanIds: z42.array(z42.string()).optional()
10628
+ });
10629
+ var LookupFillResultSchema = z42.object({
10630
+ fills: z42.array(LookupFillSchema),
10631
+ unfillable: z42.array(z42.string()),
10632
+ explanation: z42.string().optional()
10633
+ });
10634
+ var FlatPdfPlacementSchema = z42.object({
10635
+ fieldId: z42.string(),
10636
+ page: z42.number(),
10637
+ x: z42.number().describe("Percentage from left edge (0-100)"),
10638
+ y: z42.number().describe("Percentage from top edge (0-100)"),
10639
+ text: z42.string(),
10640
+ fontSize: z42.number().optional(),
10641
+ isCheckmark: z42.boolean().optional()
10642
+ });
10643
+ var AcroFormMappingSchema = z42.object({
10644
+ fieldId: z42.string(),
10645
+ acroFormName: z42.string(),
10646
+ value: z42.string()
10647
+ });
10648
+ var QualityGateStatusSchema = z42.enum(["passed", "warning", "failed"]);
10649
+ var QualitySeveritySchema = z42.enum(["info", "warning", "blocking"]);
10650
+ var ApplicationQualityIssueSchema = z42.object({
10651
+ code: z42.string(),
9573
10652
  severity: QualitySeveritySchema,
9574
- message: z41.string(),
9575
- fieldId: z41.string().optional()
10653
+ message: z42.string(),
10654
+ fieldId: z42.string().optional()
9576
10655
  });
9577
- var ApplicationQualityRoundSchema = z41.object({
9578
- round: z41.number(),
9579
- kind: z41.string(),
10656
+ var ApplicationQualityRoundSchema = z42.object({
10657
+ round: z42.number(),
10658
+ kind: z42.string(),
9580
10659
  status: QualityGateStatusSchema,
9581
- summary: z41.string().optional()
10660
+ summary: z42.string().optional()
9582
10661
  });
9583
- var ApplicationQualityArtifactSchema = z41.object({
9584
- kind: z41.string(),
9585
- label: z41.string().optional(),
9586
- itemCount: z41.number().optional()
10662
+ var ApplicationQualityArtifactSchema = z42.object({
10663
+ kind: z42.string(),
10664
+ label: z42.string().optional(),
10665
+ itemCount: z42.number().optional()
9587
10666
  });
9588
- var ApplicationEmailReviewSchema = z41.object({
9589
- issues: z41.array(ApplicationQualityIssueSchema),
10667
+ var ApplicationEmailReviewSchema = z42.object({
10668
+ issues: z42.array(ApplicationQualityIssueSchema),
9590
10669
  qualityGateStatus: QualityGateStatusSchema
9591
10670
  });
9592
- var ApplicationQualityReportSchema = z41.object({
9593
- issues: z41.array(ApplicationQualityIssueSchema),
9594
- rounds: z41.array(ApplicationQualityRoundSchema).optional(),
9595
- artifacts: z41.array(ApplicationQualityArtifactSchema).optional(),
10671
+ var ApplicationQualityReportSchema = z42.object({
10672
+ issues: z42.array(ApplicationQualityIssueSchema),
10673
+ rounds: z42.array(ApplicationQualityRoundSchema).optional(),
10674
+ artifacts: z42.array(ApplicationQualityArtifactSchema).optional(),
9596
10675
  emailReview: ApplicationEmailReviewSchema.optional(),
9597
10676
  qualityGateStatus: QualityGateStatusSchema
9598
10677
  });
9599
- var ApplicationStateSchema = z41.object({
9600
- id: z41.string(),
9601
- pdfBase64: z41.string().optional().describe("Original PDF, omitted after extraction"),
9602
- title: z41.string().optional(),
9603
- applicationType: z41.string().nullable().optional(),
9604
- fields: z41.array(ApplicationFieldSchema),
9605
- batches: z41.array(z41.array(z41.string())).optional(),
9606
- currentBatchIndex: z41.number().default(0),
10678
+ var ApplicationStateSchema = z42.object({
10679
+ id: z42.string(),
10680
+ pdfBase64: z42.string().optional().describe("Original PDF, omitted after extraction"),
10681
+ title: z42.string().optional(),
10682
+ applicationType: z42.string().nullable().optional(),
10683
+ fields: z42.array(ApplicationFieldSchema),
10684
+ batches: z42.array(z42.array(z42.string())).optional(),
10685
+ currentBatchIndex: z42.number().default(0),
9607
10686
  qualityReport: ApplicationQualityReportSchema.optional(),
9608
- status: z41.enum(["classifying", "extracting", "auto_filling", "batching", "collecting", "confirming", "mapping", "complete"]),
9609
- createdAt: z41.number(),
9610
- updatedAt: z41.number()
10687
+ status: z42.enum(["classifying", "extracting", "auto_filling", "batching", "collecting", "confirming", "mapping", "complete"]),
10688
+ createdAt: z42.number(),
10689
+ updatedAt: z42.number()
9611
10690
  });
9612
10691
 
9613
10692
  // src/application/agents/classifier.ts
@@ -10962,104 +12041,106 @@ Respond with the final answer, deduplicated citations array, overall confidence
10962
12041
  }
10963
12042
 
10964
12043
  // src/schemas/query.ts
10965
- import { z as z42 } from "zod";
10966
- var QueryIntentSchema = z42.enum([
12044
+ import { z as z43 } from "zod";
12045
+ var QueryIntentSchema = z43.enum([
10967
12046
  "policy_question",
10968
12047
  "coverage_comparison",
10969
12048
  "document_search",
10970
12049
  "claims_inquiry",
10971
12050
  "general_knowledge"
10972
12051
  ]);
10973
- var QueryAttachmentKindSchema = z42.enum(["image", "pdf", "text"]);
10974
- var QueryAttachmentSchema = z42.object({
10975
- id: z42.string().optional().describe("Optional stable attachment ID from the caller"),
12052
+ var QueryAttachmentKindSchema = z43.enum(["image", "pdf", "text"]);
12053
+ var QueryAttachmentSchema = z43.object({
12054
+ id: z43.string().optional().describe("Optional stable attachment ID from the caller"),
10976
12055
  kind: QueryAttachmentKindSchema,
10977
- name: z42.string().optional().describe("Original filename or user-facing label"),
10978
- mimeType: z42.string().optional().describe("MIME type such as image/jpeg or application/pdf"),
10979
- base64: z42.string().optional().describe("Base64-encoded file content for image/pdf attachments"),
10980
- text: z42.string().optional().describe("Plain-text attachment content when available"),
10981
- description: z42.string().optional().describe("Caller-provided description of the attachment")
12056
+ name: z43.string().optional().describe("Original filename or user-facing label"),
12057
+ mimeType: z43.string().optional().describe("MIME type such as image/jpeg or application/pdf"),
12058
+ base64: z43.string().optional().describe("Base64-encoded file content for image/pdf attachments"),
12059
+ text: z43.string().optional().describe("Plain-text attachment content when available"),
12060
+ description: z43.string().optional().describe("Caller-provided description of the attachment")
10982
12061
  });
10983
- var QueryRetrievalModeSchema = z42.enum([
12062
+ var QueryRetrievalModeSchema = z43.enum([
10984
12063
  "graph_only",
10985
12064
  "source_rag",
10986
12065
  "long_context",
10987
12066
  "hybrid"
10988
12067
  ]);
10989
- var SubQuestionSchema = z42.object({
10990
- question: z42.string().describe("Atomic sub-question to retrieve and answer independently"),
12068
+ var SubQuestionSchema = z43.object({
12069
+ question: z43.string().describe("Atomic sub-question to retrieve and answer independently"),
10991
12070
  intent: QueryIntentSchema,
10992
- chunkTypes: z42.array(z42.string()).optional().describe("Chunk types to filter retrieval (e.g. coverage, endorsement, declaration)"),
10993
- documentFilters: z42.object({
10994
- type: z42.enum(["policy", "quote"]).optional(),
10995
- carrier: z42.string().optional(),
10996
- insuredName: z42.string().optional(),
10997
- policyNumber: z42.string().optional(),
10998
- quoteNumber: z42.string().optional(),
10999
- policyTypes: z42.array(PolicyTypeSchema).optional().describe("Filter by policy type (e.g. homeowners_ho3, renters_ho4, pet) to avoid mixing up similar policies")
12071
+ chunkTypes: z43.array(z43.string()).optional().describe("Chunk types to filter retrieval (e.g. coverage, endorsement, declaration)"),
12072
+ documentFilters: z43.object({
12073
+ type: z43.enum(["policy", "quote"]).optional(),
12074
+ carrier: z43.string().optional(),
12075
+ insuredName: z43.string().optional(),
12076
+ policyNumber: z43.string().optional(),
12077
+ quoteNumber: z43.string().optional(),
12078
+ policyTypes: z43.array(PolicyTypeSchema).optional().describe("Filter by policy type (e.g. homeowners_ho3, renters_ho4, pet) to avoid mixing up similar policies")
11000
12079
  }).optional().describe("Structured filters to narrow document lookup")
11001
12080
  });
11002
- var QueryClassifyResultSchema = z42.object({
12081
+ var QueryClassifyResultSchema = z43.object({
11003
12082
  intent: QueryIntentSchema,
11004
- subQuestions: z42.array(SubQuestionSchema).min(1).describe("Decomposed atomic sub-questions"),
11005
- requiresDocumentLookup: z42.boolean().describe("Whether structured document lookup is needed"),
11006
- requiresChunkSearch: z42.boolean().describe("Whether semantic chunk search is needed"),
11007
- requiresConversationHistory: z42.boolean().describe("Whether conversation history is relevant"),
12083
+ subQuestions: z43.array(SubQuestionSchema).min(1).describe("Decomposed atomic sub-questions"),
12084
+ requiresDocumentLookup: z43.boolean().describe("Whether structured document lookup is needed"),
12085
+ requiresChunkSearch: z43.boolean().describe("Whether semantic chunk search is needed"),
12086
+ requiresConversationHistory: z43.boolean().describe("Whether conversation history is relevant"),
11008
12087
  retrievalMode: QueryRetrievalModeSchema.optional().describe("Preferred retrieval strategy for the query when source-span retrieval is available")
11009
12088
  });
11010
- var EvidenceItemSchema = z42.object({
11011
- source: z42.enum(["chunk", "document", "conversation", "attachment", "source_span"]),
11012
- chunkId: z42.string().optional(),
11013
- sourceSpanId: z42.string().optional(),
11014
- documentId: z42.string().optional(),
11015
- turnId: z42.string().optional(),
11016
- attachmentId: z42.string().optional(),
11017
- text: z42.string().describe("Text excerpt from the source"),
11018
- relevance: z42.number().min(0).max(1),
12089
+ var EvidenceItemSchema = z43.object({
12090
+ source: z43.enum(["chunk", "document", "conversation", "attachment", "source_span", "source_node"]),
12091
+ chunkId: z43.string().optional(),
12092
+ sourceNodeId: z43.string().optional(),
12093
+ sourceSpanId: z43.string().optional(),
12094
+ documentId: z43.string().optional(),
12095
+ turnId: z43.string().optional(),
12096
+ attachmentId: z43.string().optional(),
12097
+ text: z43.string().describe("Text excerpt from the source"),
12098
+ relevance: z43.number().min(0).max(1),
11019
12099
  retrievalMode: QueryRetrievalModeSchema.optional(),
11020
12100
  sourceLocation: SourceSpanLocationSchema.optional(),
11021
- metadata: z42.array(z42.object({ key: z42.string(), value: z42.string() })).optional()
11022
- });
11023
- var AttachmentInterpretationSchema = z42.object({
11024
- summary: z42.string().describe("Concise summary of what the attachment shows or contains"),
11025
- extractedFacts: z42.array(z42.string()).describe("Specific observable or document facts grounded in the attachment"),
11026
- recommendedFocus: z42.array(z42.string()).describe("Important details to incorporate when answering follow-up questions"),
11027
- confidence: z42.number().min(0).max(1)
11028
- });
11029
- var RetrievalResultSchema = z42.object({
11030
- subQuestion: z42.string(),
11031
- evidence: z42.array(EvidenceItemSchema)
11032
- });
11033
- var CitationSchema = z42.object({
11034
- index: z42.number().describe("Citation number [1], [2], etc."),
11035
- chunkId: z42.string().optional().describe("Source chunk ID, e.g. doc-123:coverage:2"),
11036
- sourceSpanId: z42.string().optional().describe("Precise source span ID when available"),
11037
- documentId: z42.string(),
11038
- documentType: z42.enum(["policy", "quote"]).optional(),
11039
- field: z42.string().optional().describe("Specific field path, e.g. coverages[0].deductible"),
11040
- quote: z42.string().describe("Exact text from source that supports the claim"),
11041
- relevance: z42.number().min(0).max(1),
12101
+ metadata: z43.array(z43.object({ key: z43.string(), value: z43.string() })).optional()
12102
+ });
12103
+ var AttachmentInterpretationSchema = z43.object({
12104
+ summary: z43.string().describe("Concise summary of what the attachment shows or contains"),
12105
+ extractedFacts: z43.array(z43.string()).describe("Specific observable or document facts grounded in the attachment"),
12106
+ recommendedFocus: z43.array(z43.string()).describe("Important details to incorporate when answering follow-up questions"),
12107
+ confidence: z43.number().min(0).max(1)
12108
+ });
12109
+ var RetrievalResultSchema = z43.object({
12110
+ subQuestion: z43.string(),
12111
+ evidence: z43.array(EvidenceItemSchema)
12112
+ });
12113
+ var CitationSchema = z43.object({
12114
+ index: z43.number().describe("Citation number [1], [2], etc."),
12115
+ chunkId: z43.string().optional().describe("Source chunk ID, e.g. doc-123:coverage:2"),
12116
+ sourceNodeId: z43.string().optional().describe("Source tree node ID when available"),
12117
+ sourceSpanId: z43.string().optional().describe("Precise source span ID when available"),
12118
+ documentId: z43.string(),
12119
+ documentType: z43.enum(["policy", "quote"]).optional(),
12120
+ field: z43.string().optional().describe("Specific field path, e.g. coverages[0].deductible"),
12121
+ quote: z43.string().describe("Exact text from source that supports the claim"),
12122
+ relevance: z43.number().min(0).max(1),
11042
12123
  retrievalMode: QueryRetrievalModeSchema.optional(),
11043
12124
  sourceLocation: SourceSpanLocationSchema.optional()
11044
12125
  });
11045
- var SubAnswerSchema = z42.object({
11046
- subQuestion: z42.string(),
11047
- answer: z42.string(),
11048
- citations: z42.array(CitationSchema),
11049
- confidence: z42.number().min(0).max(1),
11050
- needsMoreContext: z42.boolean().describe("True if evidence was insufficient to answer fully")
11051
- });
11052
- var VerifyResultSchema = z42.object({
11053
- approved: z42.boolean().describe("Whether all sub-answers are adequately grounded"),
11054
- issues: z42.array(z42.string()).describe("Specific grounding or consistency issues found"),
11055
- retrySubQuestions: z42.array(z42.string()).optional().describe("Sub-questions that need additional retrieval or re-reasoning")
11056
- });
11057
- var QueryResultSchema = z42.object({
11058
- answer: z42.string(),
11059
- citations: z42.array(CitationSchema),
12126
+ var SubAnswerSchema = z43.object({
12127
+ subQuestion: z43.string(),
12128
+ answer: z43.string(),
12129
+ citations: z43.array(CitationSchema),
12130
+ confidence: z43.number().min(0).max(1),
12131
+ needsMoreContext: z43.boolean().describe("True if evidence was insufficient to answer fully")
12132
+ });
12133
+ var VerifyResultSchema = z43.object({
12134
+ approved: z43.boolean().describe("Whether all sub-answers are adequately grounded"),
12135
+ issues: z43.array(z43.string()).describe("Specific grounding or consistency issues found"),
12136
+ retrySubQuestions: z43.array(z43.string()).optional().describe("Sub-questions that need additional retrieval or re-reasoning")
12137
+ });
12138
+ var QueryResultSchema = z43.object({
12139
+ answer: z43.string(),
12140
+ citations: z43.array(CitationSchema),
11060
12141
  intent: QueryIntentSchema,
11061
- confidence: z42.number().min(0).max(1),
11062
- followUp: z42.string().optional().describe("Suggested follow-up question if applicable")
12142
+ confidence: z43.number().min(0).max(1),
12143
+ followUp: z43.string().optional().describe("Suggested follow-up question if applicable")
11063
12144
  });
11064
12145
 
11065
12146
  // src/query/retriever.ts
@@ -11074,6 +12155,35 @@ async function retrieve(subQuestion, conversationId, config) {
11074
12155
  tasks.push(
11075
12156
  (async () => {
11076
12157
  try {
12158
+ const nodeResults = await sourceRetriever?.searchSourceNodes?.({
12159
+ question: subQuestion.question,
12160
+ limit: retrievalLimit,
12161
+ mode: retrievalMode
12162
+ }) ?? [];
12163
+ for (const result of nodeResults) {
12164
+ const hierarchyText = result.hierarchy.map((node) => `${node.path} ${node.title}: ${node.textExcerpt ?? node.description}`).join("\n");
12165
+ const spanText = result.spans.map((span) => `[source-span:${span.id}${span.pageStart ? ` p.${span.pageStart}` : ""}]
12166
+ ${span.text}`).join("\n\n");
12167
+ evidence.push({
12168
+ source: "source_node",
12169
+ sourceNodeId: result.node.id,
12170
+ sourceSpanId: result.spans[0]?.id,
12171
+ documentId: result.node.documentId,
12172
+ text: [hierarchyText, spanText].filter(Boolean).join("\n\n"),
12173
+ relevance: result.relevance,
12174
+ retrievalMode,
12175
+ sourceLocation: result.spans[0]?.location ?? (result.node.pageStart ? { page: result.node.pageStart } : void 0),
12176
+ metadata: [
12177
+ { key: "kind", value: result.node.kind },
12178
+ { key: "path", value: result.node.path },
12179
+ { key: "title", value: result.node.title },
12180
+ ...result.node.metadata ? recordToKVArray(Object.fromEntries(
12181
+ Object.entries(result.node.metadata).filter(([, value]) => typeof value === "string").map(([key, value]) => [key, value])
12182
+ )) : []
12183
+ ]
12184
+ });
12185
+ }
12186
+ if (nodeResults.length > 0) return;
11077
12187
  const sourceResults = await sourceRetriever?.searchSourceSpans({
11078
12188
  question: subQuestion.question,
11079
12189
  limit: retrievalLimit,
@@ -11093,7 +12203,7 @@ async function retrieve(subQuestion, conversationId, config) {
11093
12203
  });
11094
12204
  }
11095
12205
  } catch (e) {
11096
- await log?.(`Source span search failed for "${subQuestion.question}": ${e}`);
12206
+ await log?.(`Source tree search failed for "${subQuestion.question}": ${e}`);
11097
12207
  }
11098
12208
  })()
11099
12209
  );
@@ -11295,7 +12405,7 @@ Answer the sub-question based on the evidence above. For every factual claim, in
11295
12405
  async function reason(subQuestion, intent, evidence, config) {
11296
12406
  const { generateObject, providerOptions } = config;
11297
12407
  const evidenceText = evidence.map((e, i) => {
11298
- 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}]`;
12408
+ 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}]`;
11299
12409
  return `Evidence ${i + 1} ${sourceLabel} (relevance: ${e.relevance.toFixed(2)}):
11300
12410
  ${e.text}`;
11301
12411
  }).join("\n\n");
@@ -12097,7 +13207,7 @@ ${sa.answer}`).join("\n\n"),
12097
13207
  }
12098
13208
 
12099
13209
  // src/pce/index.ts
12100
- import { z as z43 } from "zod";
13210
+ import { z as z44 } from "zod";
12101
13211
 
12102
13212
  // src/prompts/pce/index.ts
12103
13213
  function buildPceNormalizePrompt(input) {
@@ -12131,11 +13241,11 @@ ${input.openQuestions.map((question) => `- ${question.id}${question.fieldPath ?
12131
13241
  }
12132
13242
 
12133
13243
  // src/pce/index.ts
12134
- var ReplyAnswersSchema = z43.object({
12135
- answers: z43.array(z43.object({
12136
- questionId: z43.string().optional(),
12137
- fieldPath: z43.string().optional(),
12138
- answer: z43.string()
13244
+ var ReplyAnswersSchema = z44.object({
13245
+ answers: z44.array(z44.object({
13246
+ questionId: z44.string().optional(),
13247
+ fieldPath: z44.string().optional(),
13248
+ answer: z44.string()
12139
13249
  }))
12140
13250
  });
12141
13251
  function createPceAgent(config = {}) {
@@ -12965,6 +14075,8 @@ export {
12965
14075
  DocumentMetadataSchema,
12966
14076
  DocumentNodeSchema,
12967
14077
  DocumentPageMapEntrySchema,
14078
+ DocumentSourceNodeKindSchema,
14079
+ DocumentSourceNodeSchema,
12968
14080
  DocumentTableOfContentsEntrySchema,
12969
14081
  DocumentTypeSchema,
12970
14082
  DriverRecordSchema,
@@ -13020,6 +14132,9 @@ export {
13020
14132
  MemorySourceStore,
13021
14133
  MissingInfoQuestionSchema,
13022
14134
  NamedInsuredSchema,
14135
+ OperationalCoverageLineSchema,
14136
+ OperationalEndorsementSupportSchema,
14137
+ OperationalPartySchema,
13023
14138
  PERSONAL_AUTO_USAGES,
13024
14139
  PET_SPECIES,
13025
14140
  PLATFORM_CONFIGS,
@@ -13049,6 +14164,7 @@ export {
13049
14164
  PolicyChangeStatusSchema,
13050
14165
  PolicyConditionSchema,
13051
14166
  PolicyDocumentSchema,
14167
+ PolicyOperationalProfileSchema,
13052
14168
  PolicySectionTypeSchema,
13053
14169
  PolicyTermTypeSchema,
13054
14170
  PolicyTypeSchema,
@@ -13080,6 +14196,7 @@ export {
13080
14196
  ScheduledItemCategorySchema,
13081
14197
  SectionSchema,
13082
14198
  SharedLimitSchema,
14199
+ SourceBackedValueSchema,
13083
14200
  SourceChunkSchema,
13084
14201
  SourceKindSchema,
13085
14202
  SourceSpanBBoxSchema,
@@ -13121,7 +14238,9 @@ export {
13121
14238
  buildConfirmationSummaryPrompt,
13122
14239
  buildConversationMemoryGuidance,
13123
14240
  buildCoverageGapPrompt,
14241
+ buildDeterministicOperationalProfile,
13124
14242
  buildDoclingProviderOptions,
14243
+ buildDocumentSourceTree,
13125
14244
  buildFieldExplanationPrompt,
13126
14245
  buildFieldExtractionPrompt,
13127
14246
  buildFlatPdfMappingPrompt,
@@ -13170,9 +14289,11 @@ export {
13170
14289
  getTemplate,
13171
14290
  isDoclingExtractionInput,
13172
14291
  isFileReference,
14292
+ mergeOperationalProfile,
13173
14293
  mergeQuestionAnswers,
13174
14294
  mergeSourceSpans,
13175
14295
  normalizeDoclingDocument,
14296
+ normalizeDocumentSourceTreePaths,
13176
14297
  normalizeForMatch,
13177
14298
  orderSourceEvidence,
13178
14299
  overlayTextOnPdf,