@claritylabs/cl-sdk 1.3.8 → 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
@@ -567,14 +567,20 @@ var FormReferenceSchema = z3.object({
567
567
  title: z3.string().optional(),
568
568
  formType: z3.enum(["coverage", "endorsement", "declarations", "application", "notice", "other"]),
569
569
  pageStart: z3.number().optional(),
570
- pageEnd: z3.number().optional()
570
+ pageEnd: z3.number().optional(),
571
+ documentNodeId: z3.string().optional(),
572
+ sourceSpanIds: z3.array(z3.string()).optional(),
573
+ sourceTextHash: z3.string().optional()
571
574
  });
572
575
  var TaxFeeItemSchema = z3.object({
573
576
  name: z3.string(),
574
577
  amount: z3.string(),
575
578
  amountValue: z3.number().optional(),
576
579
  type: z3.enum(["tax", "fee", "surcharge", "assessment"]).optional(),
577
- description: z3.string().optional()
580
+ description: z3.string().optional(),
581
+ documentNodeId: z3.string().optional(),
582
+ sourceSpanIds: z3.array(z3.string()).optional(),
583
+ sourceTextHash: z3.string().optional()
578
584
  });
579
585
  var RatingBasisSchema = z3.object({
580
586
  type: RatingBasisTypeSchema,
@@ -630,6 +636,7 @@ var CoverageSchema = z4.object({
630
636
  sectionRef: z4.string().optional(),
631
637
  originalContent: z4.string().optional(),
632
638
  recordId: z4.string().optional(),
639
+ documentNodeId: z4.string().optional(),
633
640
  sourceSpanIds: z4.array(z4.string()).optional(),
634
641
  sourceTextHash: z4.string().optional()
635
642
  });
@@ -659,6 +666,7 @@ var EnrichedCoverageSchema = z4.object({
659
666
  sectionRef: z4.string().optional(),
660
667
  originalContent: z4.string().optional(),
661
668
  recordId: z4.string().optional(),
669
+ documentNodeId: z4.string().optional(),
662
670
  sourceSpanIds: z4.array(z4.string()).optional(),
663
671
  sourceTextHash: z4.string().optional()
664
672
  });
@@ -687,6 +695,7 @@ var EndorsementSchema = z5.object({
687
695
  pageStart: z5.number(),
688
696
  pageEnd: z5.number().optional(),
689
697
  recordId: z5.string().optional(),
698
+ documentNodeId: z5.string().optional(),
690
699
  sourceSpanIds: z5.array(z5.string()).optional(),
691
700
  sourceTextHash: z5.string().optional()
692
701
  });
@@ -705,6 +714,7 @@ var ExclusionSchema = z6.object({
705
714
  content: z6.string(),
706
715
  pageNumber: z6.number().optional(),
707
716
  recordId: z6.string().optional(),
717
+ documentNodeId: z6.string().optional(),
708
718
  sourceSpanIds: z6.array(z6.string()).optional(),
709
719
  sourceTextHash: z6.string().optional()
710
720
  });
@@ -722,6 +732,7 @@ var PolicyConditionSchema = z7.object({
722
732
  keyValues: z7.array(ConditionKeyValueSchema).optional(),
723
733
  pageNumber: z7.number().optional(),
724
734
  recordId: z7.string().optional(),
735
+ documentNodeId: z7.string().optional(),
725
736
  sourceSpanIds: z7.array(z7.string()).optional(),
726
737
  sourceTextHash: z7.string().optional()
727
738
  });
@@ -1298,6 +1309,7 @@ var SubsectionSchema = z16.object({
1298
1309
  pageNumber: z16.number().optional(),
1299
1310
  excerpt: z16.string().optional(),
1300
1311
  content: z16.string().optional(),
1312
+ documentNodeId: z16.string().optional(),
1301
1313
  sourceSpanIds: z16.array(z16.string()).optional(),
1302
1314
  sourceTextHash: z16.string().optional()
1303
1315
  });
@@ -1312,6 +1324,7 @@ var SectionSchema = z16.object({
1312
1324
  content: z16.string().optional(),
1313
1325
  subsections: z16.array(SubsectionSchema).optional(),
1314
1326
  recordId: z16.string().optional(),
1327
+ documentNodeId: z16.string().optional(),
1315
1328
  sourceSpanIds: z16.array(z16.string()).optional(),
1316
1329
  sourceTextHash: z16.string().optional()
1317
1330
  });
@@ -1325,13 +1338,19 @@ var UnderwritingConditionSchema = z16.object({
1325
1338
  var PremiumLineSchema = z16.object({
1326
1339
  line: z16.string(),
1327
1340
  amount: z16.string(),
1328
- amountValue: z16.number().optional()
1341
+ amountValue: z16.number().optional(),
1342
+ documentNodeId: z16.string().optional(),
1343
+ sourceSpanIds: z16.array(z16.string()).optional(),
1344
+ sourceTextHash: z16.string().optional()
1329
1345
  });
1330
1346
  var AuxiliaryFactSchema = z16.object({
1331
1347
  key: z16.string(),
1332
1348
  value: z16.string(),
1333
1349
  subject: z16.string().optional(),
1334
- context: z16.string().optional()
1350
+ context: z16.string().optional(),
1351
+ documentNodeId: z16.string().optional(),
1352
+ sourceSpanIds: z16.array(z16.string()).optional(),
1353
+ sourceTextHash: z16.string().optional()
1335
1354
  });
1336
1355
  var DefinitionSchema = z16.object({
1337
1356
  term: z16.string(),
@@ -1342,6 +1361,7 @@ var DefinitionSchema = z16.object({
1342
1361
  sectionRef: z16.string().optional(),
1343
1362
  originalContent: z16.string().optional(),
1344
1363
  recordId: z16.string().optional(),
1364
+ documentNodeId: z16.string().optional(),
1345
1365
  sourceSpanIds: z16.array(z16.string()).optional(),
1346
1366
  sourceTextHash: z16.string().optional()
1347
1367
  });
@@ -1359,9 +1379,62 @@ var CoveredReasonSchema = z16.object({
1359
1379
  sectionRef: z16.string().optional(),
1360
1380
  originalContent: z16.string().optional(),
1361
1381
  recordId: z16.string().optional(),
1382
+ documentNodeId: z16.string().optional(),
1362
1383
  sourceSpanIds: z16.array(z16.string()).optional(),
1363
1384
  sourceTextHash: z16.string().optional()
1364
1385
  });
1386
+ var DocumentTableOfContentsEntrySchema = z16.object({
1387
+ title: z16.string(),
1388
+ level: z16.number().int().positive().optional(),
1389
+ pageStart: z16.number().optional(),
1390
+ pageEnd: z16.number().optional(),
1391
+ documentNodeId: z16.string().optional(),
1392
+ sourceSpanIds: z16.array(z16.string()).optional()
1393
+ });
1394
+ var DocumentPageMapEntrySchema = z16.object({
1395
+ page: z16.number().int().positive(),
1396
+ label: z16.string().optional(),
1397
+ formNumber: z16.string().optional(),
1398
+ formTitle: z16.string().optional(),
1399
+ sectionTitle: z16.string().optional(),
1400
+ extractorNames: z16.array(z16.string()).optional(),
1401
+ sourceSpanIds: z16.array(z16.string()).optional()
1402
+ });
1403
+ var DocumentAgentGuidanceSchema = z16.object({
1404
+ kind: z16.string(),
1405
+ title: z16.string(),
1406
+ detail: z16.string(),
1407
+ sourceSpanIds: z16.array(z16.string()).optional()
1408
+ });
1409
+ var DocumentMetadataSchema = z16.object({
1410
+ sourceTreeVersion: z16.string().optional(),
1411
+ sourceTreeCanonical: z16.boolean().optional(),
1412
+ formInventory: z16.array(FormReferenceSchema).optional(),
1413
+ tableOfContents: z16.array(DocumentTableOfContentsEntrySchema).optional(),
1414
+ pageMap: z16.array(DocumentPageMapEntrySchema).optional(),
1415
+ agentGuidance: z16.array(DocumentAgentGuidanceSchema).optional()
1416
+ });
1417
+ var DocumentNodeSchema = z16.lazy(
1418
+ () => z16.object({
1419
+ id: z16.string(),
1420
+ title: z16.string(),
1421
+ originalTitle: z16.string().optional(),
1422
+ type: z16.string().optional(),
1423
+ label: z16.string().optional(),
1424
+ level: z16.number().int().positive().optional(),
1425
+ sectionNumber: z16.string().optional(),
1426
+ pageStart: z16.number().optional(),
1427
+ pageEnd: z16.number().optional(),
1428
+ formNumber: z16.string().optional(),
1429
+ formTitle: z16.string().optional(),
1430
+ excerpt: z16.string().optional(),
1431
+ content: z16.string().optional(),
1432
+ interpretationLabels: z16.array(z16.string()).optional(),
1433
+ sourceSpanIds: z16.array(z16.string()).optional(),
1434
+ sourceTextHash: z16.string().optional(),
1435
+ children: z16.array(DocumentNodeSchema).optional()
1436
+ })
1437
+ );
1365
1438
  var BaseDocumentFields = {
1366
1439
  id: z16.string(),
1367
1440
  carrier: z16.string(),
@@ -1372,6 +1445,8 @@ var BaseDocumentFields = {
1372
1445
  summary: z16.string().optional(),
1373
1446
  policyTypes: z16.array(z16.string()).optional(),
1374
1447
  coverages: z16.array(CoverageSchema),
1448
+ documentMetadata: DocumentMetadataSchema,
1449
+ documentOutline: z16.array(DocumentNodeSchema),
1375
1450
  sections: z16.array(SectionSchema).optional(),
1376
1451
  definitions: z16.array(DefinitionSchema).optional(),
1377
1452
  coveredReasons: z16.array(CoveredReasonSchema).optional(),
@@ -1933,6 +2008,86 @@ var SourceChunkSchema = z20.object({
1933
2008
  pageEnd: z20.number().int().positive().optional(),
1934
2009
  metadata: z20.record(z20.string(), z20.string()).default({})
1935
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
+ });
1936
2091
 
1937
2092
  // src/source/ids.ts
1938
2093
  function normalizeText(text) {
@@ -2116,8 +2271,8 @@ function chunkSourceSpans(spans, options = {}) {
2116
2271
  if (current.length === 0) return;
2117
2272
  const text = current.map((span) => span.text).join("\n\n");
2118
2273
  const textHash = sourceSpanTextHash(text);
2119
- const pageStart = firstNumber(current.map((span) => span.pageStart));
2120
- 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));
2121
2276
  const chunk = {
2122
2277
  id: `${sanitizeIdPart(current[0].documentId)}:source_chunk:${chunks.length}:${stableHash2({
2123
2278
  sourceSpanIds: current.map((span) => span.id),
@@ -2127,8 +2282,8 @@ function chunkSourceSpans(spans, options = {}) {
2127
2282
  sourceSpanIds: current.map((span) => span.id),
2128
2283
  text,
2129
2284
  textHash,
2130
- pageStart,
2131
- pageEnd,
2285
+ pageStart: pageStart2,
2286
+ pageEnd: pageEnd2,
2132
2287
  metadata: mergeMetadata(current)
2133
2288
  };
2134
2289
  chunks.push(SourceChunkSchema.parse(chunk));
@@ -2281,6 +2436,542 @@ function matchesFilters(span, filters) {
2281
2436
  return true;
2282
2437
  }
2283
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
+
2284
2975
  // src/extraction/pdf.ts
2285
2976
  import {
2286
2977
  PDFDocument,
@@ -2670,15 +3361,15 @@ function normalizeItem(ref, item) {
2670
3361
  if (!text) return void 0;
2671
3362
  const table = getItemTable(item);
2672
3363
  const pages = (item.prov ?? []).map((prov) => getPageNumber(prov)).filter((page) => typeof page === "number" && page > 0);
2673
- const pageStart = pages.length ? Math.min(...pages) : void 0;
2674
- 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;
2675
3366
  const bboxes = (item.prov ?? []).map((prov) => toSourceSpanBBox(prov)).filter((bbox) => Boolean(bbox));
2676
3367
  return {
2677
3368
  ref,
2678
3369
  label: typeof item.label === "string" ? item.label : void 0,
2679
3370
  text,
2680
- pageStart,
2681
- pageEnd,
3371
+ pageStart: pageStart2,
3372
+ pageEnd: pageEnd2,
2682
3373
  bboxes: bboxes.length ? bboxes : void 0,
2683
3374
  table
2684
3375
  };
@@ -2690,7 +3381,7 @@ function buildSourceSpansForUnit(unit, index, options) {
2690
3381
  doclingRef: unit.ref,
2691
3382
  ...unit.label ? { doclingLabel: unit.label } : {}
2692
3383
  };
2693
- const tableId = unit.table ? `${unit.ref}:table` : void 0;
3384
+ const tableId2 = unit.table ? `${unit.ref}:table` : void 0;
2694
3385
  const tableSpan = withDoclingKind(buildSourceSpan(
2695
3386
  {
2696
3387
  documentId: options.documentId,
@@ -2700,7 +3391,7 @@ function buildSourceSpansForUnit(unit, index, options) {
2700
3391
  pageEnd: unit.pageEnd,
2701
3392
  sectionId: unit.label,
2702
3393
  sourceUnit: unit.table ? "table" : labelToSourceUnit(unit.label),
2703
- table: tableId ? { tableId } : void 0,
3394
+ table: tableId2 ? { tableId: tableId2 } : void 0,
2704
3395
  metadata: baseMetadata
2705
3396
  },
2706
3397
  index * 1e3
@@ -2724,7 +3415,7 @@ function buildSourceSpansForUnit(unit, index, options) {
2724
3415
  sourceUnit: "table_row",
2725
3416
  parentSpanId: tableSpan.id,
2726
3417
  table: {
2727
- tableId,
3418
+ tableId: tableId2,
2728
3419
  tableSpanId: tableSpan.id,
2729
3420
  rowIndex,
2730
3421
  isHeader
@@ -2732,7 +3423,7 @@ function buildSourceSpansForUnit(unit, index, options) {
2732
3423
  metadata: {
2733
3424
  ...baseMetadata,
2734
3425
  sourceUnit: "table_row",
2735
- tableId: tableId ?? "",
3426
+ tableId: tableId2 ?? "",
2736
3427
  tableSpanId: tableSpan.id,
2737
3428
  rowIndex: String(rowIndex),
2738
3429
  isHeader: String(isHeader)
@@ -2756,7 +3447,7 @@ function buildSourceSpansForUnit(unit, index, options) {
2756
3447
  sourceUnit: "table_cell",
2757
3448
  parentSpanId: rowSpan.id,
2758
3449
  table: {
2759
- tableId,
3450
+ tableId: tableId2,
2760
3451
  tableSpanId: tableSpan.id,
2761
3452
  rowSpanId: rowSpan.id,
2762
3453
  rowIndex,
@@ -2767,7 +3458,7 @@ function buildSourceSpansForUnit(unit, index, options) {
2767
3458
  metadata: {
2768
3459
  ...baseMetadata,
2769
3460
  sourceUnit: "table_cell",
2770
- tableId: tableId ?? "",
3461
+ tableId: tableId2 ?? "",
2771
3462
  tableSpanId: tableSpan.id,
2772
3463
  rowSpanId: rowSpan.id,
2773
3464
  rowIndex: String(rowIndex),
@@ -2922,7 +3613,7 @@ function buildSourceContext(spans, maxChars = 12e3) {
2922
3613
  const lines = [];
2923
3614
  let length = 0;
2924
3615
  for (const span of orderSourceSpansForContext(spans)) {
2925
- const unit = sourceUnit2(span);
3616
+ const unit = sourceUnit3(span);
2926
3617
  const table = span.table;
2927
3618
  const tableContext = [
2928
3619
  unit ? ` unit:${unit}` : "",
@@ -2946,11 +3637,11 @@ ${lines.join("\n\n")}
2946
3637
 
2947
3638
  Use sourceSpan IDs when grounding extracted contractual values.`;
2948
3639
  }
2949
- function sourceUnit2(span) {
3640
+ function sourceUnit3(span) {
2950
3641
  return span.sourceUnit ?? span.metadata?.sourceUnit;
2951
3642
  }
2952
3643
  function sourceContextRank(span) {
2953
- switch (sourceUnit2(span)) {
3644
+ switch (sourceUnit3(span)) {
2954
3645
  case "table_row":
2955
3646
  return 0;
2956
3647
  case "table":
@@ -2969,10 +3660,10 @@ function sourceContextRank(span) {
2969
3660
  }
2970
3661
  function orderSourceSpansForContext(spans) {
2971
3662
  const parentRows = new Set(
2972
- spans.filter((span) => sourceUnit2(span) === "table_row").map((span) => span.id)
3663
+ spans.filter((span) => sourceUnit3(span) === "table_row").map((span) => span.id)
2973
3664
  );
2974
3665
  const filtered = spans.filter((span) => {
2975
- if (sourceUnit2(span) !== "table_cell") return true;
3666
+ if (sourceUnit3(span) !== "table_cell") return true;
2976
3667
  const parent = span.parentSpanId ?? span.table?.rowSpanId ?? span.metadata?.rowSpanId;
2977
3668
  return !parent || !parentRows.has(parent);
2978
3669
  });
@@ -3279,6 +3970,8 @@ function assembleDocument(documentId, documentType, memory) {
3279
3970
  carrier: readRecordValue(carrier, "carrierName") ?? "Unknown",
3280
3971
  insuredName: readRecordValue(insured, "insuredName") ?? "Unknown",
3281
3972
  coverages: coverageRecords,
3973
+ documentMetadata: {},
3974
+ documentOutline: [],
3282
3975
  policyTypes: readRecordValue(classify, "policyTypes"),
3283
3976
  ...sanitizeNulls(carrier ?? {}),
3284
3977
  ...sanitizeNulls(insured ?? {}),
@@ -3330,6 +4023,247 @@ function assembleDocument(documentId, documentType, memory) {
3330
4023
  return doc;
3331
4024
  }
3332
4025
 
4026
+ // src/extraction/document-structure.ts
4027
+ var FACT_ARRAY_PATHS = [
4028
+ "coverages",
4029
+ "enrichedCoverages",
4030
+ "endorsements",
4031
+ "exclusions",
4032
+ "conditions",
4033
+ "definitions",
4034
+ "coveredReasons",
4035
+ "taxesAndFees",
4036
+ "premiumBreakdown",
4037
+ "supplementaryFacts"
4038
+ ];
4039
+ function slugPart(value) {
4040
+ const text = String(value ?? "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
4041
+ return text || "node";
4042
+ }
4043
+ function numberValue(record, ...keys) {
4044
+ for (const key of keys) {
4045
+ const value = record[key];
4046
+ if (typeof value === "number" && Number.isFinite(value)) return value;
4047
+ }
4048
+ return void 0;
4049
+ }
4050
+ function stringValue2(record, ...keys) {
4051
+ for (const key of keys) {
4052
+ const value = record[key];
4053
+ if (typeof value === "string" && value.trim()) return value.trim();
4054
+ }
4055
+ return void 0;
4056
+ }
4057
+ function stringArray(value) {
4058
+ return Array.isArray(value) ? value.filter((item) => typeof item === "string" && item.length > 0) : [];
4059
+ }
4060
+ function sourceUnit4(span) {
4061
+ return span.sourceUnit ?? span.metadata?.sourceUnit;
4062
+ }
4063
+ function spanPage(span) {
4064
+ return span.pageStart ?? span.location?.page ?? span.location?.startPage;
4065
+ }
4066
+ function spanPageEnd(span) {
4067
+ return span.pageEnd ?? span.location?.endPage ?? spanPage(span);
4068
+ }
4069
+ function sourceSpansForPage(sourceSpans, page) {
4070
+ return sourceSpans.filter((span) => spanPage(span) === page && sourceUnit4(span) === "page").map((span) => span.id);
4071
+ }
4072
+ function nodePageOverlaps(node, record) {
4073
+ const recordStart = numberValue(record, "pageNumber", "pageStart", "resolvedFromPage");
4074
+ const recordEnd = numberValue(record, "pageNumber", "pageEnd", "resolvedFromPage") ?? recordStart;
4075
+ if (!recordStart) return false;
4076
+ const nodeStart = node.pageStart ?? node.pageEnd;
4077
+ const nodeEnd = node.pageEnd ?? node.pageStart;
4078
+ if (!nodeStart) return false;
4079
+ return recordStart <= (nodeEnd ?? nodeStart) && (recordEnd ?? recordStart) >= nodeStart;
4080
+ }
4081
+ function nodeFormMatches(node, record) {
4082
+ const formNumber = stringValue2(record, "formNumber");
4083
+ return Boolean(formNumber && node.formNumber && formNumber.toLowerCase() === node.formNumber.toLowerCase());
4084
+ }
4085
+ function nodeSourceOverlaps(node, record) {
4086
+ const nodeSourceIds = new Set(node.sourceSpanIds ?? []);
4087
+ if (nodeSourceIds.size === 0) return false;
4088
+ return stringArray(record.sourceSpanIds).some((id) => nodeSourceIds.has(id));
4089
+ }
4090
+ function findBestNode(nodes, record) {
4091
+ let best;
4092
+ const visit = (node) => {
4093
+ let score = 0;
4094
+ if (nodeSourceOverlaps(node, record)) score += 8;
4095
+ if (nodeFormMatches(node, record)) score += 4;
4096
+ if (nodePageOverlaps(node, record)) score += 3;
4097
+ if (score > 0 && (!best || score > best.score)) {
4098
+ best = { node, score };
4099
+ }
4100
+ for (const child of node.children ?? []) visit(child);
4101
+ };
4102
+ for (const node of nodes) visit(node);
4103
+ return best?.node;
4104
+ }
4105
+ function attachDocumentNodeIds(document, nodes) {
4106
+ const doc = document;
4107
+ for (const path of FACT_ARRAY_PATHS) {
4108
+ const value = doc[path];
4109
+ if (!Array.isArray(value)) continue;
4110
+ doc[path] = value.map((item) => {
4111
+ if (!item || typeof item !== "object" || Array.isArray(item)) return item;
4112
+ const record = item;
4113
+ if (typeof record.documentNodeId === "string" && record.documentNodeId.length > 0) return record;
4114
+ const node = findBestNode(nodes, record);
4115
+ return node ? { ...record, documentNodeId: node.id } : record;
4116
+ });
4117
+ }
4118
+ }
4119
+ function buildNodeFromSection(section, index) {
4120
+ const title = stringValue2(section, "title", "name", "sectionRef") ?? `Section ${index + 1}`;
4121
+ const id = stringValue2(section, "documentNodeId", "recordId") ?? `section:${index}:${slugPart(title)}`;
4122
+ const pageStart2 = numberValue(section, "pageStart", "pageNumber");
4123
+ const pageEnd2 = numberValue(section, "pageEnd", "pageNumber") ?? pageStart2;
4124
+ const type = stringValue2(section, "type");
4125
+ const coverageType = stringValue2(section, "coverageType");
4126
+ const sourceSpanIds = stringArray(section.sourceSpanIds);
4127
+ const children = Array.isArray(section.subsections) ? section.subsections.filter((item) => Boolean(item) && typeof item === "object" && !Array.isArray(item)).map((subsection, childIndex) => {
4128
+ const childTitle = stringValue2(subsection, "title", "name") ?? `${title} ${childIndex + 1}`;
4129
+ const childId = stringValue2(subsection, "documentNodeId", "recordId") ?? `${id}:subsection:${childIndex}:${slugPart(childTitle)}`;
4130
+ return {
4131
+ id: childId,
4132
+ title: childTitle,
4133
+ originalTitle: childTitle,
4134
+ type,
4135
+ label: type,
4136
+ level: 2,
4137
+ sectionNumber: stringValue2(subsection, "sectionNumber"),
4138
+ pageStart: numberValue(subsection, "pageNumber") ?? pageStart2,
4139
+ pageEnd: numberValue(subsection, "pageNumber") ?? pageStart2,
4140
+ excerpt: stringValue2(subsection, "excerpt"),
4141
+ content: stringValue2(subsection, "content"),
4142
+ sourceSpanIds: stringArray(subsection.sourceSpanIds),
4143
+ sourceTextHash: stringValue2(subsection, "sourceTextHash")
4144
+ };
4145
+ }) : void 0;
4146
+ return {
4147
+ id,
4148
+ title,
4149
+ originalTitle: title,
4150
+ type,
4151
+ label: type,
4152
+ level: 1,
4153
+ sectionNumber: stringValue2(section, "sectionNumber"),
4154
+ pageStart: pageStart2,
4155
+ pageEnd: pageEnd2,
4156
+ formNumber: stringValue2(section, "formNumber"),
4157
+ formTitle: stringValue2(section, "formTitle"),
4158
+ excerpt: stringValue2(section, "excerpt"),
4159
+ content: stringValue2(section, "content"),
4160
+ interpretationLabels: [type, coverageType].filter((value) => Boolean(value)),
4161
+ sourceSpanIds: sourceSpanIds.length > 0 ? sourceSpanIds : void 0,
4162
+ sourceTextHash: stringValue2(section, "sourceTextHash"),
4163
+ children: children && children.length > 0 ? children : void 0
4164
+ };
4165
+ }
4166
+ function buildNodesFromSourceSpans(sourceSpans) {
4167
+ const candidates = sourceSpans.filter((span) => {
4168
+ const unit = sourceUnit4(span);
4169
+ return unit === "section" || unit === "section_candidate" || unit === "page";
4170
+ });
4171
+ return candidates.sort((left, right) => (spanPage(left) ?? 0) - (spanPage(right) ?? 0) || left.id.localeCompare(right.id)).map((span, index) => {
4172
+ const title = span.sectionId ?? span.formNumber ?? (sourceUnit4(span) === "page" && spanPage(span) ? `Page ${spanPage(span)}` : `Source unit ${index + 1}`);
4173
+ return {
4174
+ id: `source:${index}:${slugPart(span.id)}`,
4175
+ title,
4176
+ originalTitle: title,
4177
+ type: sourceUnit4(span),
4178
+ label: sourceUnit4(span),
4179
+ level: 1,
4180
+ pageStart: spanPage(span),
4181
+ pageEnd: spanPageEnd(span),
4182
+ formNumber: span.formNumber,
4183
+ excerpt: span.text.slice(0, 500),
4184
+ sourceSpanIds: [span.id],
4185
+ sourceTextHash: span.textHash ?? span.hash
4186
+ };
4187
+ });
4188
+ }
4189
+ function flattenToc(nodes) {
4190
+ const entries = [];
4191
+ const visit = (node) => {
4192
+ entries.push({
4193
+ title: node.title,
4194
+ level: node.level,
4195
+ pageStart: node.pageStart,
4196
+ pageEnd: node.pageEnd,
4197
+ documentNodeId: node.id,
4198
+ sourceSpanIds: node.sourceSpanIds
4199
+ });
4200
+ for (const child of node.children ?? []) visit(child);
4201
+ };
4202
+ for (const node of nodes) visit(node);
4203
+ return entries;
4204
+ }
4205
+ function buildPageMap(pageAssignments, sourceSpans, document) {
4206
+ const forms = document.formInventory ?? [];
4207
+ return pageAssignments.sort((left, right) => left.localPageNumber - right.localPageNumber).map((assignment) => {
4208
+ const form = forms.find((item) => {
4209
+ const start = item.pageStart;
4210
+ const end = item.pageEnd ?? start;
4211
+ return start != null && end != null && assignment.localPageNumber >= start && assignment.localPageNumber <= end;
4212
+ });
4213
+ return {
4214
+ page: assignment.localPageNumber,
4215
+ label: assignment.notes,
4216
+ formNumber: form?.formNumber,
4217
+ formTitle: form?.title,
4218
+ extractorNames: assignment.extractorNames,
4219
+ sourceSpanIds: sourceSpansForPage(sourceSpans, assignment.localPageNumber)
4220
+ };
4221
+ });
4222
+ }
4223
+ function buildAgentGuidance(document) {
4224
+ const guidance = [
4225
+ {
4226
+ kind: "source_structure",
4227
+ title: "Use the source outline as navigation",
4228
+ detail: "The documentOutline preserves source order and page ranges. Treat interpretation labels as hints, not as a replacement for the source document structure."
4229
+ }
4230
+ ];
4231
+ if (document.declarations) {
4232
+ guidance.push({
4233
+ kind: "declarations",
4234
+ title: "Declarations establish policy facts",
4235
+ detail: "Declarations and schedules generally establish named insured, policy period, limits, deductibles, premium, and rating facts unless modified elsewhere."
4236
+ });
4237
+ }
4238
+ if (document.endorsements?.length || document.formInventory?.some((form) => form.formType === "endorsement")) {
4239
+ guidance.push({
4240
+ kind: "endorsement_override",
4241
+ title: "Endorsements may override base terms",
4242
+ detail: "Endorsements and change forms can add, remove, or override base policy wording. When terms conflict, inspect the effective endorsement language and source page."
4243
+ });
4244
+ }
4245
+ return guidance;
4246
+ }
4247
+ function attachDocumentStructure(params) {
4248
+ const docRecord = params.document;
4249
+ const sections = Array.isArray(params.document.sections) ? params.document.sections : [];
4250
+ const existingOutline = Array.isArray(docRecord.documentOutline) ? docRecord.documentOutline : [];
4251
+ const documentOutline = existingOutline.length > 0 ? existingOutline : sections.length > 0 ? sections.map((section, index) => buildNodeFromSection(section, index)) : buildNodesFromSourceSpans(params.sourceSpans);
4252
+ if (documentOutline.length > 0) {
4253
+ attachDocumentNodeIds(params.document, documentOutline);
4254
+ }
4255
+ const metadata = {
4256
+ ...docRecord.documentMetadata && typeof docRecord.documentMetadata === "object" ? docRecord.documentMetadata : {},
4257
+ ...params.document.formInventory?.length ? { formInventory: params.document.formInventory } : {},
4258
+ ...documentOutline.length > 0 ? { tableOfContents: flattenToc(documentOutline) } : {},
4259
+ ...params.pageAssignments.length > 0 ? { pageMap: buildPageMap(params.pageAssignments, params.sourceSpans, params.document) } : {},
4260
+ agentGuidance: buildAgentGuidance(params.document)
4261
+ };
4262
+ docRecord.documentMetadata = metadata;
4263
+ docRecord.documentOutline = documentOutline;
4264
+ return params.document;
4265
+ }
4266
+
3333
4267
  // src/prompts/coordinator/format.ts
3334
4268
  function buildFormatPrompt(entries) {
3335
4269
  const block = entries.map((e) => `===ENTRY ${e.id}===
@@ -3929,6 +4863,8 @@ function chunkDocument(doc) {
3929
4863
  {
3930
4864
  formNumber: form.formNumber,
3931
4865
  formType: form.formType,
4866
+ documentNodeId: form.documentNodeId,
4867
+ sourceSpanIds: form.sourceSpanIds?.join(","),
3932
4868
  documentType: doc.type
3933
4869
  }
3934
4870
  );
@@ -3955,6 +4891,7 @@ function chunkDocument(doc) {
3955
4891
  formNumber: end.formNumber,
3956
4892
  pageStart: end.pageStart,
3957
4893
  pageEnd: end.pageEnd,
4894
+ documentNodeId: end.documentNodeId,
3958
4895
  sourceSpanIds: end.sourceSpanIds?.join(","),
3959
4896
  sourceTextHash: end.sourceTextHash,
3960
4897
  documentType: doc.type
@@ -3966,6 +4903,8 @@ function chunkDocument(doc) {
3966
4903
  ${exc.content}`.trim(), {
3967
4904
  formNumber: exc.formNumber,
3968
4905
  pageNumber: exc.pageNumber,
4906
+ documentNodeId: exc.documentNodeId,
4907
+ sourceSpanIds: exc.sourceSpanIds?.join(","),
3969
4908
  documentType: doc.type
3970
4909
  });
3971
4910
  });
@@ -3983,6 +4922,8 @@ ${exc.content}`.trim(), {
3983
4922
  conditionName: cond.name,
3984
4923
  conditionType: cond.conditionType,
3985
4924
  pageNumber: cond.pageNumber,
4925
+ documentNodeId: cond.documentNodeId,
4926
+ sourceSpanIds: cond.sourceSpanIds?.join(","),
3986
4927
  documentType: doc.type
3987
4928
  }
3988
4929
  );
@@ -4004,6 +4945,9 @@ ${exc.content}`.trim(), {
4004
4945
  formTitle: firstString2(definition, ["formTitle"]),
4005
4946
  pageNumber: typeof definition.pageNumber === "number" ? definition.pageNumber : void 0,
4006
4947
  sectionRef: firstString2(definition, ["sectionRef", "sectionTitle"]),
4948
+ documentNodeId: firstString2(definition, ["documentNodeId"]),
4949
+ sourceSpanIds: Array.isArray(definition.sourceSpanIds) ? definition.sourceSpanIds.join(",") : void 0,
4950
+ sourceTextHash: firstString2(definition, ["sourceTextHash"]),
4007
4951
  documentType: doc.type
4008
4952
  }
4009
4953
  );
@@ -4032,6 +4976,9 @@ ${exc.content}`.trim(), {
4032
4976
  formTitle: firstString2(coveredReason, ["formTitle"]),
4033
4977
  pageNumber: typeof coveredReason.pageNumber === "number" ? coveredReason.pageNumber : void 0,
4034
4978
  sectionRef: firstString2(coveredReason, ["sectionRef", "sectionTitle"]),
4979
+ documentNodeId: firstString2(coveredReason, ["documentNodeId"]),
4980
+ sourceSpanIds: Array.isArray(coveredReason.sourceSpanIds) ? coveredReason.sourceSpanIds.join(",") : void 0,
4981
+ sourceTextHash: firstString2(coveredReason, ["sourceTextHash"]),
4035
4982
  documentType: doc.type
4036
4983
  }
4037
4984
  );
@@ -4093,6 +5040,7 @@ ${declLines.join("\n")}`, declMeta);
4093
5040
  evidenceKind: "navigation",
4094
5041
  sectionType: sec.type,
4095
5042
  sectionNumber: sec.sectionNumber,
5043
+ documentNodeId: sec.documentNodeId,
4096
5044
  pageStart: sec.pageStart,
4097
5045
  pageEnd: sec.pageEnd,
4098
5046
  sourceSpanIds: sec.sourceSpanIds?.join(","),
@@ -4116,6 +5064,7 @@ ${declLines.join("\n")}`, declMeta);
4116
5064
  sectionType: sec.type,
4117
5065
  parentSection: sec.title,
4118
5066
  sectionNumber: sub.sectionNumber,
5067
+ documentNodeId: sub.documentNodeId,
4119
5068
  pageNumber: sub.pageNumber,
4120
5069
  sourceSpanIds: sub.sourceSpanIds?.join(","),
4121
5070
  sourceTextHash: sub.sourceTextHash,
@@ -4124,6 +5073,31 @@ ${declLines.join("\n")}`, declMeta);
4124
5073
  );
4125
5074
  });
4126
5075
  });
5076
+ asRecordArray(extendedDoc.documentOutline).forEach((node, i) => {
5077
+ const title = firstString2(node, ["title", "originalTitle"]) ?? `Document Node ${i + 1}`;
5078
+ const children = asRecordArray(node.children);
5079
+ pushChunk(
5080
+ `section:outline:${i}`,
5081
+ "section",
5082
+ lines([
5083
+ `Document Outline: ${title}`,
5084
+ firstString2(node, ["label", "type"]) ? `Label: ${firstString2(node, ["label", "type"])}` : null,
5085
+ typeof node.pageStart === "number" ? `Pages: ${node.pageStart}${typeof node.pageEnd === "number" ? `-${node.pageEnd}` : ""}` : null,
5086
+ firstString2(node, ["excerpt", "content"]),
5087
+ children.length > 0 ? `Children: ${children.map((child, childIndex) => firstString2(child, ["title", "originalTitle"]) ?? `Child ${childIndex + 1}`).join(", ")}` : null
5088
+ ]),
5089
+ {
5090
+ evidenceKind: "navigation",
5091
+ documentNodeId: firstString2(node, ["id"]),
5092
+ sectionType: firstString2(node, ["type", "label"]),
5093
+ pageStart: typeof node.pageStart === "number" ? node.pageStart : void 0,
5094
+ pageEnd: typeof node.pageEnd === "number" ? node.pageEnd : void 0,
5095
+ sourceSpanIds: Array.isArray(node.sourceSpanIds) ? node.sourceSpanIds.join(",") : void 0,
5096
+ sourceTextHash: firstString2(node, ["sourceTextHash"]),
5097
+ documentType: doc.type
5098
+ }
5099
+ );
5100
+ });
4127
5101
  doc.locations?.forEach((loc, i) => {
4128
5102
  chunks.push({
4129
5103
  id: `${docId}:location:${i}`,
@@ -6947,13 +7921,13 @@ async function resolveReferentialCoverages(params) {
6947
7921
  function textValue(value) {
6948
7922
  return typeof value === "string" && value.trim() ? value.trim() : void 0;
6949
7923
  }
6950
- function numberValue(value) {
7924
+ function numberValue2(value) {
6951
7925
  return typeof value === "number" && Number.isFinite(value) ? value : void 0;
6952
7926
  }
6953
7927
  function normalize(value) {
6954
7928
  return value.toLowerCase().replace(/&/g, "and").replace(/[^a-z0-9]+/g, " ").trim();
6955
7929
  }
6956
- function sourceUnit3(span) {
7930
+ function sourceUnit5(span) {
6957
7931
  return span.sourceUnit ?? span.metadata?.sourceUnit;
6958
7932
  }
6959
7933
  function pageNumber(span) {
@@ -7023,7 +7997,7 @@ function firstField(fields, patterns) {
7023
7997
  }
7024
7998
  function coverageFromRow(span) {
7025
7999
  const rowText = span.text.trim();
7026
- if (!rowText || sourceUnit3(span) !== "table_row") return void 0;
8000
+ if (!rowText || sourceUnit5(span) !== "table_row") return void 0;
7027
8001
  if (span.table?.isHeader || span.metadata?.isHeader === "true") return void 0;
7028
8002
  const fields = splitRowFields(rowText);
7029
8003
  let name;
@@ -7085,7 +8059,7 @@ function coverageKey(coverage) {
7085
8059
  textValue(coverage.name) ?? "",
7086
8060
  textValue(coverage.limit) ?? "",
7087
8061
  textValue(coverage.limitType) ?? "",
7088
- numberValue(coverage.pageNumber) ?? ""
8062
+ numberValue2(coverage.pageNumber) ?? ""
7089
8063
  ].map((part) => normalize(String(part))).join("|");
7090
8064
  }
7091
8065
  function rowMatchesExisting(row, existing) {
@@ -7103,7 +8077,7 @@ function recoverCoverageScheduleRows(params) {
7103
8077
  const payload = params.memory.get("coverage_limits");
7104
8078
  const existing = Array.isArray(payload?.coverages) ? payload.coverages : [];
7105
8079
  const pages = coveragePages(params.pageAssignments);
7106
- const candidates = params.sourceSpans.filter((span) => sourceUnit3(span) === "table_row").filter((span) => {
8080
+ const candidates = params.sourceSpans.filter((span) => sourceUnit5(span) === "table_row").filter((span) => {
7107
8081
  const page = pageNumber(span);
7108
8082
  return page !== void 0 && pages.has(page);
7109
8083
  }).map(coverageFromRow).filter((coverage) => Boolean(coverage));
@@ -7739,13 +8713,13 @@ function buildPlanFromPageAssignments(pageAssignments, pageCount, formInventory)
7739
8713
  const expanded = new Set(pages);
7740
8714
  for (const page of pages) {
7741
8715
  for (const form of contextualForms) {
7742
- const pageStart = form.pageStart;
7743
- const pageEnd = form.pageEnd ?? form.pageStart;
8716
+ const pageStart2 = form.pageStart;
8717
+ const pageEnd2 = form.pageEnd ?? form.pageStart;
7744
8718
  const formType = form.formType;
7745
8719
  const supportsContextualExpansion = extractorName === "endorsements" ? formType === "endorsement" : formType === "coverage" || formType === "endorsement";
7746
8720
  if (!supportsContextualExpansion) continue;
7747
- if (page < pageStart || page > pageEnd) continue;
7748
- for (let current = pageStart; current <= pageEnd; current += 1) {
8721
+ if (page < pageStart2 || page > pageEnd2) continue;
8722
+ for (let current = pageStart2; current <= pageEnd2; current += 1) {
7749
8723
  expanded.add(current);
7750
8724
  }
7751
8725
  }
@@ -7778,7 +8752,10 @@ var ARRAY_PATHS = [
7778
8752
  { memoryKey: "sections", arrayKeys: ["sections"] },
7779
8753
  { memoryKey: "definitions", arrayKeys: ["definitions"] },
7780
8754
  { memoryKey: "covered_reasons", arrayKeys: ["coveredReasons", "covered_reasons"] },
7781
- { memoryKey: "declarations", arrayKeys: ["fields"] }
8755
+ { memoryKey: "declarations", arrayKeys: ["fields"] },
8756
+ { memoryKey: "premium_breakdown", arrayKeys: ["premiumBreakdown", "taxesAndFees"] },
8757
+ { memoryKey: "supplementary", arrayKeys: ["auxiliaryFacts", "supplementaryFacts"] },
8758
+ { memoryKey: "form_inventory", arrayKeys: ["forms"] }
7782
8759
  ];
7783
8760
  function normalize2(value) {
7784
8761
  return value.replace(/\s+/g, " ").trim().toLowerCase();
@@ -7790,7 +8767,7 @@ function textValue2(record, ...keys) {
7790
8767
  }
7791
8768
  return void 0;
7792
8769
  }
7793
- function numberValue2(record, ...keys) {
8770
+ function numberValue3(record, ...keys) {
7794
8771
  for (const key of keys) {
7795
8772
  const value = record[key];
7796
8773
  if (typeof value === "number" && Number.isFinite(value)) return value;
@@ -7823,11 +8800,11 @@ function textMatches(record, span) {
7823
8800
  function sourceHashFor(spans) {
7824
8801
  return spans.map((span) => span.textHash ?? span.hash).filter(Boolean).join(":") || void 0;
7825
8802
  }
7826
- function sourceUnit4(span) {
8803
+ function sourceUnit6(span) {
7827
8804
  return span.sourceUnit ?? span.metadata?.sourceUnit;
7828
8805
  }
7829
8806
  function hierarchyScore(span) {
7830
- switch (sourceUnit4(span)) {
8807
+ switch (sourceUnit6(span)) {
7831
8808
  case "table_row":
7832
8809
  return 5;
7833
8810
  case "table":
@@ -7850,8 +8827,8 @@ function preferParentRows(matches, sourceSpans) {
7850
8827
  const expanded = [];
7851
8828
  const seen = /* @__PURE__ */ new Set();
7852
8829
  for (const match of matches) {
7853
- const parent = sourceUnit4(match) === "table_cell" ? byId.get(parentRowId(match) ?? "") : void 0;
7854
- const preferred = parent && sourceUnit4(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;
7855
8832
  if (seen.has(preferred.id)) continue;
7856
8833
  seen.add(preferred.id);
7857
8834
  expanded.push(preferred);
@@ -7860,11 +8837,11 @@ function preferParentRows(matches, sourceSpans) {
7860
8837
  }
7861
8838
  function findSourceSpansForRecord(record, sourceSpans) {
7862
8839
  if (sourceSpans.length === 0) return [];
7863
- const pageStart = numberValue2(record, "pageNumber", "pageStart");
7864
- const pageEnd = numberValue2(record, "pageNumber", "pageEnd");
8840
+ const pageStart2 = numberValue3(record, "pageNumber", "pageStart");
8841
+ const pageEnd2 = numberValue3(record, "pageNumber", "pageEnd");
7865
8842
  const scored = sourceSpans.map((span) => {
7866
8843
  let score = 0;
7867
- if (pageOverlaps(pageStart, pageEnd, span)) score += 4;
8844
+ if (pageOverlaps(pageStart2, pageEnd2, span)) score += 4;
7868
8845
  if (formMatches(record, span)) score += 3;
7869
8846
  if (textMatches(record, span)) score += 2;
7870
8847
  if (score > 0) score += hierarchyScore(span);
@@ -7903,6 +8880,422 @@ function groundExtractionMemoryWithSourceSpans(memory, sourceSpans) {
7903
8880
  }
7904
8881
  }
7905
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
+
7906
9299
  // src/extraction/coordinator.ts
7907
9300
  function createExtractor(config) {
7908
9301
  const {
@@ -8088,17 +9481,17 @@ ${span.text}` : span.text;
8088
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);
8089
9482
  return {
8090
9483
  sections: candidateSpans.map((span, index) => {
8091
- const pageStart = span.pageStart ?? span.location?.startPage ?? span.location?.page ?? startPage;
8092
- const pageEnd = span.pageEnd ?? span.location?.endPage ?? pageStart;
8093
- 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}`;
8094
9487
  return {
8095
9488
  title,
8096
9489
  sectionNumber: span.formNumber,
8097
- pageStart,
8098
- pageEnd,
9490
+ pageStart: pageStart2,
9491
+ pageEnd: pageEnd2,
8099
9492
  type: inferSectionType(title, span.text),
8100
9493
  excerpt: span.text.slice(0, 240),
8101
- recordId: `section_index_${pageStart}_${index}`,
9494
+ recordId: `section_index_${pageStart2}_${index}`,
8102
9495
  sourceSpanIds: [span.id],
8103
9496
  sourceTextHash: span.textHash ?? sourceSpanTextHash(span.text)
8104
9497
  };
@@ -8237,6 +9630,51 @@ ${span.text}` : span.text;
8237
9630
  await sourceStore.addSourceChunks(sourceChunks);
8238
9631
  }
8239
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
+ }
8240
9678
  const pipelineCtx = createPipelineContext({
8241
9679
  id,
8242
9680
  onSave: onCheckpointSave,
@@ -8804,6 +10242,7 @@ ${pageText}`;
8804
10242
  }));
8805
10243
  onProgress?.("Assembling document...");
8806
10244
  const document = assembleDocument(id, documentType, memory);
10245
+ attachDocumentStructure({ document, pageAssignments, sourceSpans });
8807
10246
  await pipelineCtx.save("assemble", {
8808
10247
  id,
8809
10248
  pageCount,
@@ -9106,8 +10545,8 @@ Respond with JSON only:
9106
10545
  }`;
9107
10546
 
9108
10547
  // src/schemas/application.ts
9109
- import { z as z41 } from "zod";
9110
- var FieldTypeSchema = z41.enum([
10548
+ import { z as z42 } from "zod";
10549
+ var FieldTypeSchema = z42.enum([
9111
10550
  "text",
9112
10551
  "numeric",
9113
10552
  "currency",
@@ -9116,138 +10555,138 @@ var FieldTypeSchema = z41.enum([
9116
10555
  "table",
9117
10556
  "declaration"
9118
10557
  ]);
9119
- var ApplicationFieldSchema = z41.object({
9120
- id: z41.string(),
9121
- label: z41.string(),
9122
- section: z41.string(),
10558
+ var ApplicationFieldSchema = z42.object({
10559
+ id: z42.string(),
10560
+ label: z42.string(),
10561
+ section: z42.string(),
9123
10562
  fieldType: FieldTypeSchema,
9124
- required: z41.boolean(),
9125
- options: z41.array(z41.string()).optional(),
9126
- columns: z41.array(z41.string()).optional(),
9127
- requiresExplanationIfYes: z41.boolean().optional(),
9128
- condition: z41.object({
9129
- dependsOn: z41.string(),
9130
- 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()
9131
10570
  }).optional(),
9132
- value: z41.string().optional(),
9133
- source: z41.string().optional().describe("Where the value came from: auto-fill, user, lookup"),
9134
- confidence: z41.enum(["confirmed", "high", "medium", "low"]).optional(),
9135
- sourceSpanIds: z41.array(z41.string()).optional().describe("Stable source spans that support the field value or field anchor"),
9136
- userSourceSpanIds: z41.array(z41.string()).optional().describe("Message or attachment spans that support user-provided values"),
9137
- pageNumber: z41.number().int().positive().optional().describe("Application page where the field label or anchor appears"),
9138
- fieldAnchorId: z41.string().optional().describe("Stable field anchor ID derived from page, section, label, and form metadata"),
9139
- acroFormName: z41.string().optional().describe("Native PDF AcroForm field name when available"),
9140
- validationStatus: z41.enum(["valid", "needs_review", "unsupported", "missing"]).optional()
9141
- });
9142
- var ApplicationClassifyResultSchema = z41.object({
9143
- isApplication: z41.boolean(),
9144
- confidence: z41.number().min(0).max(1),
9145
- applicationType: z41.string().nullable()
9146
- });
9147
- var FieldExtractionResultSchema = z41.object({
9148
- fields: z41.array(ApplicationFieldSchema)
9149
- });
9150
- var AutoFillMatchSchema = z41.object({
9151
- fieldId: z41.string(),
9152
- value: z41.string(),
9153
- confidence: z41.enum(["confirmed"]),
9154
- contextKey: z41.string()
9155
- });
9156
- var AutoFillResultSchema = z41.object({
9157
- matches: z41.array(AutoFillMatchSchema)
9158
- });
9159
- var QuestionBatchResultSchema = z41.object({
9160
- batches: z41.array(z41.array(z41.string()).describe("Array of field IDs in this batch"))
9161
- });
9162
- var LookupRequestSchema = z41.object({
9163
- type: z41.string().describe("Type of lookup: 'records', 'website', 'policy'"),
9164
- description: z41.string(),
9165
- url: z41.string().optional(),
9166
- targetFieldIds: z41.array(z41.string())
9167
- });
9168
- var ReplyIntentSchema = z41.object({
9169
- primaryIntent: z41.enum(["answers_only", "question", "lookup_request", "mixed"]),
9170
- hasAnswers: z41.boolean(),
9171
- questionText: z41.string().optional(),
9172
- questionFieldIds: z41.array(z41.string()).optional(),
9173
- lookupRequests: z41.array(LookupRequestSchema).optional()
9174
- });
9175
- var ParsedAnswerSchema = z41.object({
9176
- fieldId: z41.string(),
9177
- value: z41.string(),
9178
- explanation: z41.string().optional()
9179
- });
9180
- var AnswerParsingResultSchema = z41.object({
9181
- answers: z41.array(ParsedAnswerSchema),
9182
- unanswered: z41.array(z41.string()).describe("Field IDs that were not answered")
9183
- });
9184
- var LookupFillSchema = z41.object({
9185
- fieldId: z41.string(),
9186
- value: z41.string(),
9187
- source: z41.string().describe("Specific citable reference, e.g. 'GL Policy #POL-12345 (Hartford)'"),
9188
- sourceSpanIds: z41.array(z41.string()).optional()
9189
- });
9190
- var LookupFillResultSchema = z41.object({
9191
- fills: z41.array(LookupFillSchema),
9192
- unfillable: z41.array(z41.string()),
9193
- explanation: z41.string().optional()
9194
- });
9195
- var FlatPdfPlacementSchema = z41.object({
9196
- fieldId: z41.string(),
9197
- page: z41.number(),
9198
- x: z41.number().describe("Percentage from left edge (0-100)"),
9199
- y: z41.number().describe("Percentage from top edge (0-100)"),
9200
- text: z41.string(),
9201
- fontSize: z41.number().optional(),
9202
- isCheckmark: z41.boolean().optional()
9203
- });
9204
- var AcroFormMappingSchema = z41.object({
9205
- fieldId: z41.string(),
9206
- acroFormName: z41.string(),
9207
- value: z41.string()
9208
- });
9209
- var QualityGateStatusSchema = z41.enum(["passed", "warning", "failed"]);
9210
- var QualitySeveritySchema = z41.enum(["info", "warning", "blocking"]);
9211
- var ApplicationQualityIssueSchema = z41.object({
9212
- 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(),
9213
10652
  severity: QualitySeveritySchema,
9214
- message: z41.string(),
9215
- fieldId: z41.string().optional()
10653
+ message: z42.string(),
10654
+ fieldId: z42.string().optional()
9216
10655
  });
9217
- var ApplicationQualityRoundSchema = z41.object({
9218
- round: z41.number(),
9219
- kind: z41.string(),
10656
+ var ApplicationQualityRoundSchema = z42.object({
10657
+ round: z42.number(),
10658
+ kind: z42.string(),
9220
10659
  status: QualityGateStatusSchema,
9221
- summary: z41.string().optional()
10660
+ summary: z42.string().optional()
9222
10661
  });
9223
- var ApplicationQualityArtifactSchema = z41.object({
9224
- kind: z41.string(),
9225
- label: z41.string().optional(),
9226
- itemCount: z41.number().optional()
10662
+ var ApplicationQualityArtifactSchema = z42.object({
10663
+ kind: z42.string(),
10664
+ label: z42.string().optional(),
10665
+ itemCount: z42.number().optional()
9227
10666
  });
9228
- var ApplicationEmailReviewSchema = z41.object({
9229
- issues: z41.array(ApplicationQualityIssueSchema),
10667
+ var ApplicationEmailReviewSchema = z42.object({
10668
+ issues: z42.array(ApplicationQualityIssueSchema),
9230
10669
  qualityGateStatus: QualityGateStatusSchema
9231
10670
  });
9232
- var ApplicationQualityReportSchema = z41.object({
9233
- issues: z41.array(ApplicationQualityIssueSchema),
9234
- rounds: z41.array(ApplicationQualityRoundSchema).optional(),
9235
- 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(),
9236
10675
  emailReview: ApplicationEmailReviewSchema.optional(),
9237
10676
  qualityGateStatus: QualityGateStatusSchema
9238
10677
  });
9239
- var ApplicationStateSchema = z41.object({
9240
- id: z41.string(),
9241
- pdfBase64: z41.string().optional().describe("Original PDF, omitted after extraction"),
9242
- title: z41.string().optional(),
9243
- applicationType: z41.string().nullable().optional(),
9244
- fields: z41.array(ApplicationFieldSchema),
9245
- batches: z41.array(z41.array(z41.string())).optional(),
9246
- 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),
9247
10686
  qualityReport: ApplicationQualityReportSchema.optional(),
9248
- status: z41.enum(["classifying", "extracting", "auto_filling", "batching", "collecting", "confirming", "mapping", "complete"]),
9249
- createdAt: z41.number(),
9250
- updatedAt: z41.number()
10687
+ status: z42.enum(["classifying", "extracting", "auto_filling", "batching", "collecting", "confirming", "mapping", "complete"]),
10688
+ createdAt: z42.number(),
10689
+ updatedAt: z42.number()
9251
10690
  });
9252
10691
 
9253
10692
  // src/application/agents/classifier.ts
@@ -10602,104 +12041,106 @@ Respond with the final answer, deduplicated citations array, overall confidence
10602
12041
  }
10603
12042
 
10604
12043
  // src/schemas/query.ts
10605
- import { z as z42 } from "zod";
10606
- var QueryIntentSchema = z42.enum([
12044
+ import { z as z43 } from "zod";
12045
+ var QueryIntentSchema = z43.enum([
10607
12046
  "policy_question",
10608
12047
  "coverage_comparison",
10609
12048
  "document_search",
10610
12049
  "claims_inquiry",
10611
12050
  "general_knowledge"
10612
12051
  ]);
10613
- var QueryAttachmentKindSchema = z42.enum(["image", "pdf", "text"]);
10614
- var QueryAttachmentSchema = z42.object({
10615
- 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"),
10616
12055
  kind: QueryAttachmentKindSchema,
10617
- name: z42.string().optional().describe("Original filename or user-facing label"),
10618
- mimeType: z42.string().optional().describe("MIME type such as image/jpeg or application/pdf"),
10619
- base64: z42.string().optional().describe("Base64-encoded file content for image/pdf attachments"),
10620
- text: z42.string().optional().describe("Plain-text attachment content when available"),
10621
- 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")
10622
12061
  });
10623
- var QueryRetrievalModeSchema = z42.enum([
12062
+ var QueryRetrievalModeSchema = z43.enum([
10624
12063
  "graph_only",
10625
12064
  "source_rag",
10626
12065
  "long_context",
10627
12066
  "hybrid"
10628
12067
  ]);
10629
- var SubQuestionSchema = z42.object({
10630
- 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"),
10631
12070
  intent: QueryIntentSchema,
10632
- chunkTypes: z42.array(z42.string()).optional().describe("Chunk types to filter retrieval (e.g. coverage, endorsement, declaration)"),
10633
- documentFilters: z42.object({
10634
- type: z42.enum(["policy", "quote"]).optional(),
10635
- carrier: z42.string().optional(),
10636
- insuredName: z42.string().optional(),
10637
- policyNumber: z42.string().optional(),
10638
- quoteNumber: z42.string().optional(),
10639
- 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")
10640
12079
  }).optional().describe("Structured filters to narrow document lookup")
10641
12080
  });
10642
- var QueryClassifyResultSchema = z42.object({
12081
+ var QueryClassifyResultSchema = z43.object({
10643
12082
  intent: QueryIntentSchema,
10644
- subQuestions: z42.array(SubQuestionSchema).min(1).describe("Decomposed atomic sub-questions"),
10645
- requiresDocumentLookup: z42.boolean().describe("Whether structured document lookup is needed"),
10646
- requiresChunkSearch: z42.boolean().describe("Whether semantic chunk search is needed"),
10647
- 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"),
10648
12087
  retrievalMode: QueryRetrievalModeSchema.optional().describe("Preferred retrieval strategy for the query when source-span retrieval is available")
10649
12088
  });
10650
- var EvidenceItemSchema = z42.object({
10651
- source: z42.enum(["chunk", "document", "conversation", "attachment", "source_span"]),
10652
- chunkId: z42.string().optional(),
10653
- sourceSpanId: z42.string().optional(),
10654
- documentId: z42.string().optional(),
10655
- turnId: z42.string().optional(),
10656
- attachmentId: z42.string().optional(),
10657
- text: z42.string().describe("Text excerpt from the source"),
10658
- 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),
10659
12099
  retrievalMode: QueryRetrievalModeSchema.optional(),
10660
12100
  sourceLocation: SourceSpanLocationSchema.optional(),
10661
- metadata: z42.array(z42.object({ key: z42.string(), value: z42.string() })).optional()
10662
- });
10663
- var AttachmentInterpretationSchema = z42.object({
10664
- summary: z42.string().describe("Concise summary of what the attachment shows or contains"),
10665
- extractedFacts: z42.array(z42.string()).describe("Specific observable or document facts grounded in the attachment"),
10666
- recommendedFocus: z42.array(z42.string()).describe("Important details to incorporate when answering follow-up questions"),
10667
- confidence: z42.number().min(0).max(1)
10668
- });
10669
- var RetrievalResultSchema = z42.object({
10670
- subQuestion: z42.string(),
10671
- evidence: z42.array(EvidenceItemSchema)
10672
- });
10673
- var CitationSchema = z42.object({
10674
- index: z42.number().describe("Citation number [1], [2], etc."),
10675
- chunkId: z42.string().optional().describe("Source chunk ID, e.g. doc-123:coverage:2"),
10676
- sourceSpanId: z42.string().optional().describe("Precise source span ID when available"),
10677
- documentId: z42.string(),
10678
- documentType: z42.enum(["policy", "quote"]).optional(),
10679
- field: z42.string().optional().describe("Specific field path, e.g. coverages[0].deductible"),
10680
- quote: z42.string().describe("Exact text from source that supports the claim"),
10681
- 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),
10682
12123
  retrievalMode: QueryRetrievalModeSchema.optional(),
10683
12124
  sourceLocation: SourceSpanLocationSchema.optional()
10684
12125
  });
10685
- var SubAnswerSchema = z42.object({
10686
- subQuestion: z42.string(),
10687
- answer: z42.string(),
10688
- citations: z42.array(CitationSchema),
10689
- confidence: z42.number().min(0).max(1),
10690
- needsMoreContext: z42.boolean().describe("True if evidence was insufficient to answer fully")
10691
- });
10692
- var VerifyResultSchema = z42.object({
10693
- approved: z42.boolean().describe("Whether all sub-answers are adequately grounded"),
10694
- issues: z42.array(z42.string()).describe("Specific grounding or consistency issues found"),
10695
- retrySubQuestions: z42.array(z42.string()).optional().describe("Sub-questions that need additional retrieval or re-reasoning")
10696
- });
10697
- var QueryResultSchema = z42.object({
10698
- answer: z42.string(),
10699
- 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),
10700
12141
  intent: QueryIntentSchema,
10701
- confidence: z42.number().min(0).max(1),
10702
- 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")
10703
12144
  });
10704
12145
 
10705
12146
  // src/query/retriever.ts
@@ -10714,6 +12155,35 @@ async function retrieve(subQuestion, conversationId, config) {
10714
12155
  tasks.push(
10715
12156
  (async () => {
10716
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;
10717
12187
  const sourceResults = await sourceRetriever?.searchSourceSpans({
10718
12188
  question: subQuestion.question,
10719
12189
  limit: retrievalLimit,
@@ -10733,7 +12203,7 @@ async function retrieve(subQuestion, conversationId, config) {
10733
12203
  });
10734
12204
  }
10735
12205
  } catch (e) {
10736
- await log?.(`Source span search failed for "${subQuestion.question}": ${e}`);
12206
+ await log?.(`Source tree search failed for "${subQuestion.question}": ${e}`);
10737
12207
  }
10738
12208
  })()
10739
12209
  );
@@ -10935,7 +12405,7 @@ Answer the sub-question based on the evidence above. For every factual claim, in
10935
12405
  async function reason(subQuestion, intent, evidence, config) {
10936
12406
  const { generateObject, providerOptions } = config;
10937
12407
  const evidenceText = evidence.map((e, i) => {
10938
- 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}]`;
10939
12409
  return `Evidence ${i + 1} ${sourceLabel} (relevance: ${e.relevance.toFixed(2)}):
10940
12410
  ${e.text}`;
10941
12411
  }).join("\n\n");
@@ -11737,7 +13207,7 @@ ${sa.answer}`).join("\n\n"),
11737
13207
  }
11738
13208
 
11739
13209
  // src/pce/index.ts
11740
- import { z as z43 } from "zod";
13210
+ import { z as z44 } from "zod";
11741
13211
 
11742
13212
  // src/prompts/pce/index.ts
11743
13213
  function buildPceNormalizePrompt(input) {
@@ -11771,11 +13241,11 @@ ${input.openQuestions.map((question) => `- ${question.id}${question.fieldPath ?
11771
13241
  }
11772
13242
 
11773
13243
  // src/pce/index.ts
11774
- var ReplyAnswersSchema = z43.object({
11775
- answers: z43.array(z43.object({
11776
- questionId: z43.string().optional(),
11777
- fieldPath: z43.string().optional(),
11778
- 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()
11779
13249
  }))
11780
13250
  });
11781
13251
  function createPceAgent(config = {}) {
@@ -12601,6 +14071,13 @@ export {
12601
14071
  DeductibleTypeSchema,
12602
14072
  DefenseCostTreatmentSchema,
12603
14073
  DefinitionSchema,
14074
+ DocumentAgentGuidanceSchema,
14075
+ DocumentMetadataSchema,
14076
+ DocumentNodeSchema,
14077
+ DocumentPageMapEntrySchema,
14078
+ DocumentSourceNodeKindSchema,
14079
+ DocumentSourceNodeSchema,
14080
+ DocumentTableOfContentsEntrySchema,
12604
14081
  DocumentTypeSchema,
12605
14082
  DriverRecordSchema,
12606
14083
  DwellingDetailsSchema,
@@ -12655,6 +14132,9 @@ export {
12655
14132
  MemorySourceStore,
12656
14133
  MissingInfoQuestionSchema,
12657
14134
  NamedInsuredSchema,
14135
+ OperationalCoverageLineSchema,
14136
+ OperationalEndorsementSupportSchema,
14137
+ OperationalPartySchema,
12658
14138
  PERSONAL_AUTO_USAGES,
12659
14139
  PET_SPECIES,
12660
14140
  PLATFORM_CONFIGS,
@@ -12684,6 +14164,7 @@ export {
12684
14164
  PolicyChangeStatusSchema,
12685
14165
  PolicyConditionSchema,
12686
14166
  PolicyDocumentSchema,
14167
+ PolicyOperationalProfileSchema,
12687
14168
  PolicySectionTypeSchema,
12688
14169
  PolicyTermTypeSchema,
12689
14170
  PolicyTypeSchema,
@@ -12715,6 +14196,7 @@ export {
12715
14196
  ScheduledItemCategorySchema,
12716
14197
  SectionSchema,
12717
14198
  SharedLimitSchema,
14199
+ SourceBackedValueSchema,
12718
14200
  SourceChunkSchema,
12719
14201
  SourceKindSchema,
12720
14202
  SourceSpanBBoxSchema,
@@ -12756,7 +14238,9 @@ export {
12756
14238
  buildConfirmationSummaryPrompt,
12757
14239
  buildConversationMemoryGuidance,
12758
14240
  buildCoverageGapPrompt,
14241
+ buildDeterministicOperationalProfile,
12759
14242
  buildDoclingProviderOptions,
14243
+ buildDocumentSourceTree,
12760
14244
  buildFieldExplanationPrompt,
12761
14245
  buildFieldExtractionPrompt,
12762
14246
  buildFlatPdfMappingPrompt,
@@ -12805,9 +14289,11 @@ export {
12805
14289
  getTemplate,
12806
14290
  isDoclingExtractionInput,
12807
14291
  isFileReference,
14292
+ mergeOperationalProfile,
12808
14293
  mergeQuestionAnswers,
12809
14294
  mergeSourceSpans,
12810
14295
  normalizeDoclingDocument,
14296
+ normalizeDocumentSourceTreePaths,
12811
14297
  normalizeForMatch,
12812
14298
  orderSourceEvidence,
12813
14299
  overlayTextOnPdf,